Difference between revisions of "Modifying Contexts (advanced)"
(Reorganize the page, and lay out new sections, remove old material that is now elsewhere. Write first section on number formats with examples.) |
(Wrote Lists and Delimiters section) |
||
Line 87: | Line 87: | ||
=== Lists and Delimiters === |
=== Lists and Delimiters === |
||
− | This section and the next section 'Parens' are closely related. |
||
+ | The Context object contains two more collections of data that were not mentioned in the [[Introduction to Contexts]]: the <code>lists</code> and <code>parens</code> objects. These are closely related, and determine what types of objects are created from various delimiters like braces and brackets. For example, in some contexts parentheses form Points, while in others they form Intervals or Lists. This it controlled by the settings in these two objects. |
||
− | WebWork considers the following objects to be types of lists: |
||
− | * Point, |
||
− | * Vector, |
||
− | * Matrix, |
||
− | * List, |
||
− | * Interval, |
||
− | * Set, |
||
− | * Union, |
||
− | * AbsoluteValue. |
||
− | |||
− | The most common modification made to lists are to which type of parentheses is used to enclose them. |
||
− | The purpose of the following description is meant to make you aware of how the various parenthesis types are used by default. |
||
− | * Points by default look like e.g. (3,4). |
||
+ | The <code>lists</code> object contains the definitions for the various types of list-like objects such as Points, Vectors, and Intervals. Each of the types of list has an entry that tells the parser what class implements the list, and specifies the open and close delimiters and the separators that will be used by default to display an instance of the class. A special case is <code>AbsoluteValue</code>, which is treated as a list since it has open and close delimiters, even though the list can only contain one element. |
||
− | * Vectors by default look like e.g., <3,4,5>. |
||
− | * Matrix objects by default look like ~[[2,3],[2,3]]. |
||
− | * A list by default looks like 3, 4, 5. An interval by default looks like (0,9), 0,9), etc. |
||
− | * A set by default looks like {3,4,5}. |
||
− | * A union by default looks like (-infinity,0? U (5,7]. |
||
− | * Absolute value by default looks like |-5|. |
||
− | Next we'll discuss how to modify the type of parentheses used with the various objects. |
||
+ | {| class="wikitable" |
||
+ | ! List !! style="padding:5px" | Open !! style="padding:5px" | Close !! style="padding:5px" | Separator |
||
− | WebWork recognizes the full range of parentheses types, as explained above (e.g., (, <, [, { ). But by default their meanings are dependent on the context. You can change how this works. |
||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px" | <code>Point</code> |
||
+ | | style="padding:5px; text-align:center" | <code>(</code> |
||
+ | | style="padding:5px; text-align:center" | <code>)</code> |
||
+ | | style="padding:5px; text-align:center" | <code>,</code> |
||
− | For example, this command will cause Vector objects to look like (3,4,5) instead of <3,4,5>: |
||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px" | <code>Vector</code> |
||
+ | | style="padding:5px; text-align:center" | <code><</code> |
||
+ | | style="padding:5px; text-align:center" | <code>></code> |
||
+ | | style="padding:5px; text-align:center" | <code>,</code> |
||
− | Context()->parens->set('('=>{type=>'Vector'}); |
||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px" | <code>Matrix</code> |
||
+ | | style="padding:5px; text-align:center" | <code>[</code> |
||
+ | | style="padding:5px; text-align:center" | <code>]</code> |
||
+ | | style="padding:5px; text-align:center" | <code>,</code> |
||
+ | |||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px" | <code>List</code> |
||
+ | | style="padding:5px; text-align:center" | <code></code> |
||
+ | | style="padding:5px; text-align:center" | <code></code> |
||
+ | | style="padding:5px; text-align:center" | <code>,</code> |
||
+ | |||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px" | <code>Interval</code> |
||
+ | | style="padding:5px; text-align:center" | <code>(</code> |
||
+ | | style="padding:5px; text-align:center" | <code>)</code> |
||
+ | | style="padding:5px; text-align:center" | <code>,</code> |
||
+ | |||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px" | <code>Set</code> |
||
+ | | style="padding:5px; text-align:center" | <code>{</code> |
||
+ | | style="padding:5px; text-align:center" | <code>}</code> |
||
+ | | style="padding:5px; text-align:center" | <code>,</code> |
||
+ | |||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px" | <code>Union</code> |
||
+ | | style="padding:5px; text-align:center" | <code></code> |
||
+ | | style="padding:5px; text-align:center" | <code></code> |
||
+ | | style="padding:5px; text-align:center" | <code>U</code> |
||
+ | |||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px" | <code>AbsoluteValue</code> |
||
+ | | style="padding:5px; text-align:center" | <code>|</code> |
||
+ | | style="padding:5px; text-align:center" | <code>|</code> |
||
+ | | style="padding:5px; text-align:center" | <code></code> |
||
+ | |||
+ | |} |
||
+ | |||
+ | This delimiters listed in this table are used when the object doesn't specify the delimiters explicitly via its <code>{open}</code> and <code>{close}</code> properties. This is usually the case when objects are created via the class constructors rather than parsing a string. For example, <code>Vector(4,0,-1)</code> would not have its <code>{open}</code> and <code>{close}</code> properties set, so would use the defaults in the <code>lists</code> object. Note that the Interval object has parentheses as its default delimiters, but the <code>Interval()</code> constructor will set the open and close properties automatically so that you can form open and closed intervals easily: |
||
+ | |||
+ | $I1 = Interval(1,2); # an open interval |
||
+ | $I2 = Interval([1,2]); # a closed interval |
||
+ | $I3 = Interval("(",1,2,"]"); # a half-open interval |
||
+ | |||
+ | It is also possible to put the delimiters at the end of the interval (which is how the Interval's <code>value()</code> method returns them): <code>Interval(0,1,"(","]")</code>. |
||
+ | |||
+ | On the other hand, instances of these objects created by parsing a string usually save the open and closing delimiters in the object's <code>{open}</code> and <code>{close}</code> properties, so the default will not be used in those cases. E.g., <code>$v = Compute("<4,0,-1>")</code> would produce a Vector object with the <code>$v->{open} = "<"</code> and <code>$v->{close} = ">"</code>. |
||
+ | |||
+ | To change the <code>list</code> settings, use the <code>set()</code> method, as usual: |
||
+ | |||
+ | Context()->lists->set(Vector => {open => "(", close => ")"}; |
||
+ | |||
+ | Note that this only affects the case where the Vector object doesn't specify the open and close delimiters explicitly. It also doesn't change the delimiters that the parser uses to identify a vector, since the <code>list</code> values are only for output. |
||
+ | |||
+ | To change what delimiter to use for a given object type, you need to change the <code>parens</code> object. This associates each open delimiter to one of the <code>list</code> types given above, and also gives the close delimiter that is needed to match it, and some other data about how it can be used. So to complete the change for Vectors, we would need to use |
||
+ | |||
+ | Context()->parens->set("(" => {type => "Vector", close => ")"}); |
||
+ | |||
+ | The other possible data for a <code>paren</code> object includes: |
||
+ | |||
+ | {| class="wikitable" |
||
+ | ! Name !! Description |
||
+ | |||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px; white-space:nowrap" | <code>type=>"<i>name</i>"</code> |
||
+ | | style="padding:5px" | Specifies the list type that this open delimiter will genarate. |
||
+ | |||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px; white-space:nowrap" | <code>close=>"<i>c</i>"</code></code> |
||
+ | | style="padding:5px" | The closing delimiter for this opening one. |
||
+ | |||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px; white-space:nowrap" | <code>removable=>1</code> or <code>0</code> |
||
+ | | style="padding:5px" | Do/don't remove delimiters when used around a single element. When <code>1</code>, don't create a list, just return the element. |
||
+ | |||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px; white-space:nowrap" | <code>formInterval=>"<i>c</i>"</code> |
||
+ | | style="padding:5px" | When present, this indicates that an Interval should be formed when the list is closed by the character <code><i>c</i></code> rather than the usual <code>close</code> character. |
||
+ | |||
+ | |- style="vertical-align:top" |
||
+ | | style="padding:5px; white-space:nowrap" | <code>emptyOK=>1</code> or <code>0</code> |
||
+ | | style="padding:5px" | Do/don't allow empty delimiters. When <code>0</code> there must be at least one element between the open and close delimiters. |
||
+ | |||
+ | |} |
||
=== More about Variables === |
=== More about Variables === |
Revision as of 13:15, 12 August 2012
Contents
Advanced Context Modifications
The Introduction to Contexts describes how to make basic modifications to a Context's variables, constants, strings, flags, functions, operators, and reduction rules. Here we will describe more advanced modifications and techniques involving the Context.
Number Formats
Real numbers are stored using a format that retains about 16 or 17 significant digits, making computations very accurate in most situations. When a number is displayed, you probably don't want to see all 17 digits (that would make a vector in three-space take up around 35 characters, for example). To make answers easier to read, MathObjects usually display only 6 significant digits. You can change the format used, however, to suit your needs. The format is determined by the Context()->{format}{number}
, which is a printf
-style string indicating how real numbers should be formatted for display.
The format always should begin with %
and end with one of f
, e
, or g
, possibly followed by #
. Here, f
means fixed-point notation (e.g. 452.116
), e
means exponential notation (e.g, 3.578E-5
), and g
means use the form most appropriate for the magnitude of the number. Between the %
and the letter you can (optionally) include .n
where n
is the number of decimal digits to use for the number. If the format ends in #
, then trailing zeros are removed after the number is formatted. (More sophisticated formats are possible, but this describes the basics.)
Context()->{format}{number} = "%.2f"; # format numbers using 2-place decimals (e.g., for currency values). Context()->{format}{number} = "%.4f#"; # format numbers using 4-place decimals, but remove trailing zeros, if any.
The default format is "%g"
.
The Context also includes information about what should count as a number when an answer is parsed. There are two patterns for this, a signed number and an unsigned number. The latter is what is used in parsing numbers (and the sign is treated as unary minus); former is used in the Value::matchNumber()
function. These are stored in the Context()->{pattern}
hash; the default values are:
Context()->{pattern}{number} = '(?:\d+(?:\.\d*)?|\.\d+)(?:E[-+]?\d+)?'; Context()->{pattern}{signedNumber} = '[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:E[-+]?\d+)?';
These are fairly complicated regular expressions that match the usual fixe-point and exponential notation for numbers in WeBWorK. It is possible to change these patterns to handle things like commas instead of decimals for European usage, or to allow commas every three digits. Note, however, that you would need to include a NumberCheck
routine that would translate the special format into the required internal format. For example, this allows you to enter numbers as hexadecimal values:
# # Numbers in hexadecimal # Context()->{pattern}{number} = '[0-9A-F]+'; Context()->{pattern}{signedNumber = '[-+]?[0-9A-F]+'; Context()->flags->set(NumberCheck => sub { my $self = shift; # the Number object $self->{value} = hex($self->{value_string}); # convert hex to decimal via perl hex() function $self->{isOne} = ($self->{value} == 1); # set marker indicating if the value is 1 $self->{isZero} = ($self->{value} == 0); # set marker indicating if the value is 0 }); Context()->update;
Note that after changing the pattern
you must call Context()->update
to remake the tokenization patterns used by the Context.
Here is an example that lets you use commas in your numbers:
# # Allow commas every three digits in numbers # Context()->{pattern}{number} = '(:?(:?\d{1,3}(:?\,\d{3})+|\d+)(?:\.\d*)?|\.\d+)(?:E[-+]?\d+)?'; Context()->{pattern}{signedNumber} = '[-+]?(:?(:?\d{1,3}(:?\,\d{3})+|\d+)(?:\.\d*)?|\.\d+)(?:E[-+]?\d+)?'; Context()->flags->set(NumberCheck => sub { my $self = shift; # the Number object my $value = $self->{value_string}; # the original string $value =~ s/,//g; # remove commas $self->{value} = $value + 0; # make sure it is converted to a number $self->{isOne} = ($self->{value} == 1); # set marker indicating if the value is 1 $self->{isZero} = ($self->{value} == 0); # set marker indicating if the value is 0 }); Context()->update;
If you want to make the numbers display with commas, then you will need to subclass the Value::Real
object and override the string()
and TeX()
methods to insert the commas again, and then tie your new class into the Context()->{value}{Real}
value. For example, in addition to the changes above, you might do
# # Subclass the Value::Real class and override its string() and TeX() # methods to insert commas back into the output # package my::Real; our @ISA = ('Value::Real'); # subclass of this Value::Real sub string { my $self = shift; my $x = $self->SUPER::string(@_); # get the original string output my ($n,@rest) = split(/([.E])/,$x,1); # break it into the integer part and the rest while ($n =~ m/[0-9]{4}(,|$)/) # add commas as needed {$n =~ s/([0-9])([0-9]{3})(,|$)/$1,$2$3/} return join("",$n,@rest); # return the final string } sub TeX { my $self = shift; my $n = $self->SUPER::TeX(@_); # original TeX uses string(), so commas are already there $n =~ s/,/{,}/g; # just make sure they have the correct spacing return $n; } package main; # end of package my::Real; Context()->{value}{Real} = "my::Real"; # make the Context use my::Real rather then Value::Real Context()->{format}{number} = "%f#"; # format using "f" rather than "g", so no exponential notation
This could be put into a separate macro file that you could load into your problems whenever it is needed. See Creating Custom Contexts for details.
Lists and Delimiters
The Context object contains two more collections of data that were not mentioned in the Introduction to Contexts: the lists
and parens
objects. These are closely related, and determine what types of objects are created from various delimiters like braces and brackets. For example, in some contexts parentheses form Points, while in others they form Intervals or Lists. This it controlled by the settings in these two objects.
The lists
object contains the definitions for the various types of list-like objects such as Points, Vectors, and Intervals. Each of the types of list has an entry that tells the parser what class implements the list, and specifies the open and close delimiters and the separators that will be used by default to display an instance of the class. A special case is AbsoluteValue
, which is treated as a list since it has open and close delimiters, even though the list can only contain one element.
List | Open | Close | Separator |
---|---|---|---|
Point
|
(
|
)
|
,
|
Vector
|
<
|
>
|
,
|
Matrix
|
[
|
]
|
,
|
List
|
|
|
,
|
Interval
|
(
|
)
|
,
|
Set
|
{
|
}
|
,
|
Union
|
|
|
U
|
AbsoluteValue
|
|
|
|
|
|
This delimiters listed in this table are used when the object doesn't specify the delimiters explicitly via its {open}
and {close}
properties. This is usually the case when objects are created via the class constructors rather than parsing a string. For example, Vector(4,0,-1)
would not have its {open}
and {close}
properties set, so would use the defaults in the lists
object. Note that the Interval object has parentheses as its default delimiters, but the Interval()
constructor will set the open and close properties automatically so that you can form open and closed intervals easily:
$I1 = Interval(1,2); # an open interval $I2 = Interval([1,2]); # a closed interval $I3 = Interval("(",1,2,"]"); # a half-open interval
It is also possible to put the delimiters at the end of the interval (which is how the Interval's value()
method returns them): Interval(0,1,"(","]")
.
On the other hand, instances of these objects created by parsing a string usually save the open and closing delimiters in the object's {open}
and {close}
properties, so the default will not be used in those cases. E.g., $v = Compute("<4,0,-1>")
would produce a Vector object with the $v->{open} = "<"
and $v->{close} = ">"
.
To change the list
settings, use the set()
method, as usual:
Context()->lists->set(Vector => {open => "(", close => ")"};
Note that this only affects the case where the Vector object doesn't specify the open and close delimiters explicitly. It also doesn't change the delimiters that the parser uses to identify a vector, since the list
values are only for output.
To change what delimiter to use for a given object type, you need to change the parens
object. This associates each open delimiter to one of the list
types given above, and also gives the close delimiter that is needed to match it, and some other data about how it can be used. So to complete the change for Vectors, we would need to use
Context()->parens->set("(" => {type => "Vector", close => ")"});
The other possible data for a paren
object includes:
Name | Description |
---|---|
type=>"name"
|
Specifies the list type that this open delimiter will genarate. |
close=>"c"
|
The closing delimiter for this opening one. |
removable=>1 or 0
|
Do/don't remove delimiters when used around a single element. When 1 , don't create a list, just return the element.
|
formInterval=>"c"
|
When present, this indicates that an Interval should be formed when the list is closed by the character c rather than the usual close character.
|
emptyOK=>1 or 0
|
Do/don't allow empty delimiters. When 0 there must be at least one element between the open and close delimiters.
|
More about Variables
More about Constants
The list of predefined constants is e, pi, i, j, k. The constant i denotes sqrt(-1) in Context("Complex"), denotes the vector <1,0> in Context("Vector2D"), and denotes the vector <1,0,0> in Context("Vector"), Context("Matrix"), and Context("Point"). The constant i is undefined outside of those contexts. The constants j and k are <0,1,0> and <0,0,1>, respectively, in Context("Vector") and Context("Matrix"). The constant j is <0,1> in Context("Vector2D"), and k is undefined there. The constants i, j and k are undefined outside of the contexts described above.
Context()->constants->set(i => {TeX=>'\boldsymbol{i}', perl=>'i'}); Context()->constants->remove("k"); Context()->constants->set(R => {TeX => '{\bf R}'});
Adding New Functions
Adding New Operators
Error Messages
Course-Wide Customization
See also
- Introduction to Contexts
- Context flags
- Reduction rules for MathObject Formulas
- Context Operator Table
- Common Contexts