From: Sam H. v. a. <we...@ma...> - 2007-10-03 18:55:18
|
Log Message: ----------- reformatted documentation for the rest of the MathObjects-related macros Modified Files: -------------- pg/macros: parserImplicitEquation.pl parserImplicitPlane.pl parserMultiAnswer.pl parserMultiPart.pl parserNumberWithUnits.pl parserParametricLine.pl parserPopUp.pl parserRadioButtons.pl parserSolutionFor.pl parserVectorUtils.pl problemPreserveAnswers.pl problemRandomize.pl Revision Data ------------- Index: parserRadioButtons.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserRadioButtons.pl,v retrieving revision 1.8 retrieving revision 1.9 diff -Lmacros/parserRadioButtons.pl -Lmacros/parserRadioButtons.pl -u -r1.8 -r1.9 --- macros/parserRadioButtons.pl +++ macros/parserRadioButtons.pl @@ -1,73 +1,87 @@ -loadMacros('MathObjects.pl','contextString.pl'); +=head1 NAME -sub _parserRadioButtons_init {parserRadioButtons::Init()}; # don't reload this file +parserRadioButtons.pl - Radio buttons compatible with Value objects, specifically MultiAnswer objects. =head1 DESCRIPTION - #################################################################### - # - # This file implements a radio button group object that is compatible - # with Value objects, and in particular, with the MultiPart object. - # - # To create a RadioButtons object, use - # - # $radio = RadioButtons([choices,...],correct,options); - # - # where "choices" are the strings for the items in the radio buttons, - # "correct" is the choice that is the correct answer for the group, - # and options are chosen from among: - # - # labels => [label1,...] Specifies the text to be used - # as the student answer for each - # entry in the radio group. - # This can also be set to the string - # "ABC" to get lettered labels or - # "123" to get numbered labels. - # The default is to use a few words - # from the text string for each button. - # - # separator => string text to put between the radio - # buttons. - # Default: $BR - # - # checked => choice the text or index (starting at zero) - # of the button to be checked - # Default: none checked - # - # maxLabelSize => n the approximate largest size that should - # be used for the answer strings to be - # generated by the radio buttons (if - # the choice strings are too long, they - # will be trimmed and "..." inserted) - # Default: 25 - # - # uncheckable => 0 or 1 determines whether the radio buttons can - # or "shift" be unchecked (requires JavaScript). - # To uncheck, click a second time; when - # set to "shift", unchecking requires the - # shift key to be pressed. - # Default: 0 - # - # - # To insert the radio buttons into the problem text, use - # - # BEGIN_TEXT - # \{$radio->buttons\} - # END_TEXT - # - # and then - # - # ANS($radio->cmp); - # - # to get the answer checker for the radion buttons. - # - # You can use the RadioButtons object in MultiPart objects. This is - # the reason for the RadioButton's ans_rule method (since that is what - # MultiPart calls to get answer rules). - # +This file implements a radio button group object that is compatible +with Value objects, and in particular, with the MultiAnswer object. + +To create a RadioButtons object, use + + $radio = RadioButtons([choices,...],correct,options); + +where "choices" are the strings for the items in the radio buttons, +"correct" is the choice that is the correct answer for the group, +and options are chosen from among: + +=over + +=item C<S<< labels => [label1,...] >>> + +Specifies the text to be used +as the student answer for each +entry in the radio group. +This can also be set to the string +"ABC" to get lettered labels or +"123" to get numbered labels. +The default is to use a few words +from the text string for each button. + +=item C<S<< separator => string >>> + +text to put between the radio +buttons. +Default: $BR + +=item C<S<< checked => choice >>> + +the text or index (starting at zero) +of the button to be checked +Default: none checked + +=item C<S<< maxLabelSize => n >>> + +the approximate largest size that should +be used for the answer strings to be +generated by the radio buttons (if +the choice strings are too long, they +will be trimmed and "..." inserted) +Default: 25 + +=item C<S<< uncheckable => 0 or 1 or "shift" >>> + +determines whether the radio buttons can +be unchecked (requires JavaScript). +To uncheck, click a second time; when +set to "shift", unchecking requires the +shift key to be pressed. +Default: 0 + +=back + +To insert the radio buttons into the problem text, use + + BEGIN_TEXT + \{$radio->buttons\} + END_TEXT + +and then + + ANS($radio->cmp); + +to get the answer checker for the radion buttons. + +You can use the RadioButtons object in MultiPart objects. This is +the reason for the RadioButton's ans_rule method (since that is what +MultiPart calls to get answer rules). =cut +loadMacros('MathObjects.pl','contextString.pl'); + +sub _parserRadioButtons_init {parserRadioButtons::Init()}; # don't reload this file + ################################################## # # The package that implements RadioButtons Index: parserImplicitEquation.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserImplicitEquation.pl,v retrieving revision 1.9 retrieving revision 1.10 diff -Lmacros/parserImplicitEquation.pl -Lmacros/parserImplicitEquation.pl -u -r1.9 -r1.10 --- macros/parserImplicitEquation.pl +++ macros/parserImplicitEquation.pl @@ -1,132 +1,173 @@ -loadMacros("MathObjects.pl"); +=head1 NAME -sub _parserImplicitEquation_init {ImplicitEquation::Init()}; # don't reload this file +parserImplicitEquation.pl - An answer checker for implicit equations. =head1 DESCRIPTION - ###################################################################### - # - # This is a MathObject class that implements an answer checker for - # implicitly defined equations. The checker looks for the zeros of - # the equation and tests that the student and professor equations - # both have the same solutions. - # - # This type of check is very subtle, and there are important issues - # that you may have to take into account. The solutions to the - # equations are found numerically, and so they will not be exact; - # that means that there are tolerances that may need to be adjusted - # for your particular equation. Also, it is always possible for the - # student to represent the function in a form that will exceed those - # tolerances, and so be marked as incorrect. The answer checker - # attempts to set the parameters based on the values of the functions - # involved, but this may not work perfectly. - # - # The method used to locate the solutions of A=B is by finding zeros - # of A-B, and it requires this function to take on both positive and - # negative values (that is, it can only find transverse intersections - # of the surface A-B=0 and the plane at height 0). For example, even - # though the solutions of (x^2+y^1-1)^2=0 form a circle, the - # algorithm will fail to find any solutions for this equation. - # - # In order to locate the zeros, you may need to change the limits so - # that they include regions where the function is both positive and - # negative (see below). The algorithm will avoid discontinuities, so - # you can specify things like x-y=1/(x+y) rather than x^2-y^2=1. - # - # Because the solutions are found using a random search, it is - # possible the randomly chosen starting points don't locate enough - # zeros for the function, in which case the check will fail. This - # can happen for both the professor's function and the student's, - # since zeros are found for both. This means that a correct answer - # can sometimes be marked incorrect depending on the random points - # chosen initially. These points also affect the values selected for - # the tolerances used to determine when a function's value is zero, - # and so can affect whether the student's function is marked as - # correct or not. - # - # If an equation has several components or branches, it is possible - # that the random location of solutions will not find zeros on some - # of the branches, and so might incorrectly mark as correct an - # equation that only is zero on one of the components. For example, - # x^2-y^2=0 has solutions along the lines y=x and y=-x, so it is - # possible that x-y=0 or x+y=0 will be marked as correct if the - # random points are unluckily chosen. One way to reduce this problem - # is to increase the number of solutions that are required (by - # setting the ImplicitPoints flag in the Context). Another is to - # specify the solutions yourself, so that you are sure there are - # points on each component. - # - # These problems should be rare, and the values for the various - # parameters have been set in an attempt to minimize the possibility - # of these errors, but they can occur, and you should be aware of - # them, and their possible solutions. - # - # - # Usage examples: - # - # Context("ImplicitEquation"); - # $f = ImplicitEquation("x^2 = cos(y)"); - # $f = ImplicitEquation("x^2 - 2y^2 = 5",limits=>[[-3,3],[-2,2]]); - # $f = ImplicitEquation("x=1/y",tolerance=>.0001); - # - # Then use - # - # ANS($f->cmp); - # - # to get the answer checker for $f. - # - # There are a number of Context flags that control the answer checker. - # These include: - # - # ImplicitPoints => 7 (the number of solutions to test) - # ImplicitTolerance => 1E-6 (relative tolerance value for when - # the tested function is zero) - # ImplicitAbsoluteMinTolerance => 1E-3 (the minimum tolerance allowed) - # ImplicitAbsoluteMaxTolerance => 1E-3 (the maximum tolerance allowed) - # ImplicitPointTolerance => 1E-9 (relative tolerance for how close - # the solution point must be to an - # actual solution) - # BisectionTolerance => .01 (extra factor used for the tolerance - # when finding the solutions) - # BisectionCutoff => 40 (maximum number of bisections to - # perform when looking for a solution) - # - # You may set any of these using Context()->flags->set(...). - # - # In addition to the Context flags, you can set some values within - # the ImplicitEquation itself: - # - # tolerance (the absolute tolerance for zeros of the function) - # bisect_tolerance (the tolerance used when searching for zeros) - # point_tolerance (the absolute tolerance for how close to an - # actual solution the located solution must be) - # limits (the domain to use for the function; see the - # documentation for the Formula object) - # solutions (a reference to an array of references to arrays - # that contain the coordinates of the points - # that are the solutions of the equation) - # - # These can be set in the in the ImplicitEquation() call that creates - # the object, as in the examples below: - # - # For example: - # - # $f = ImplicitEquation("x^2-y^2=0", - # solutions => [[0,0],[1,1],[-1,1],[-1,-1],[1,-1]], - # tolerance => .001 - # ); - # - # - # $f = ImplicitEquation("xy=5",limits=>[-3,3]); - # - # The limits value can be set globally within the Context, if you wish, - # and the others can be controlled by the Context flags discussed - # above. - # - ###################################################################### +This is a MathObject class that implements an answer checker for +implicitly defined equations. The checker looks for the zeros of +the equation and tests that the student and professor equations +both have the same solutions. + +This type of check is very subtle, and there are important issues +that you may have to take into account. The solutions to the +equations are found numerically, and so they will not be exact; +that means that there are tolerances that may need to be adjusted +for your particular equation. Also, it is always possible for the +student to represent the function in a form that will exceed those +tolerances, and so be marked as incorrect. The answer checker +attempts to set the parameters based on the values of the functions +involved, but this may not work perfectly. + +The method used to locate the solutions of A=B is by finding zeros +of A-B, and it requires this function to take on both positive and +negative values (that is, it can only find transverse intersections +of the surface A-B=0 and the plane at height 0). For example, even +though the solutions of (x^2+y^1-1)^2=0 form a circle, the +algorithm will fail to find any solutions for this equation. + +In order to locate the zeros, you may need to change the limits so +that they include regions where the function is both positive and +negative (see below). The algorithm will avoid discontinuities, so +you can specify things like x-y=1/(x+y) rather than x^2-y^2=1. + +Because the solutions are found using a random search, it is +possible the randomly chosen starting points don't locate enough +zeros for the function, in which case the check will fail. This +can happen for both the professor's function and the student's, +since zeros are found for both. This means that a correct answer +can sometimes be marked incorrect depending on the random points +chosen initially. These points also affect the values selected for +the tolerances used to determine when a function's value is zero, +and so can affect whether the student's function is marked as +correct or not. + +If an equation has several components or branches, it is possible +that the random location of solutions will not find zeros on some +of the branches, and so might incorrectly mark as correct an +equation that only is zero on one of the components. For example, +x^2-y^2=0 has solutions along the lines y=x and y=-x, so it is +possible that x-y=0 or x+y=0 will be marked as correct if the +random points are unluckily chosen. One way to reduce this problem +is to increase the number of solutions that are required (by +setting the ImplicitPoints flag in the Context). Another is to +specify the solutions yourself, so that you are sure there are +points on each component. + +These problems should be rare, and the values for the various +parameters have been set in an attempt to minimize the possibility +of these errors, but they can occur, and you should be aware of +them, and their possible solutions. + +Usage examples: + + Context("ImplicitEquation"); + $f = ImplicitEquation("x^2 = cos(y)"); + $f = ImplicitEquation("x^2 - 2y^2 = 5",limits=>[[-3,3],[-2,2]]); + $f = ImplicitEquation("x=1/y",tolerance=>.0001); + +Then use + + ANS($f->cmp); + +to get the answer checker for $f. + +There are a number of Context flags that control the answer checker. +These include: + +=over + +=item C<S<< ImplicitPoints => 7 >>> + +the number of solutions to test. + +=item C<S<< ImplicitTolerance => 1E-6 >>> + +relative tolerance value for when +the tested function is zero. + +=item C<S<< ImplicitAbsoluteMinTolerance => 1E-3 >>> + +the minimum tolerance allowed. + +=item C<S<< ImplicitAbsoluteMaxTolerance => 1E-3 >>> + +the maximum tolerance allowed. + +=item C<S<< ImplicitPointTolerance => 1E-9 >>> + +relative tolerance for how close +the solution point must be to an +actual solution. + +=item C<S<< BisectionTolerance => .01 >>> + +extra factor used for the tolerance +when finding the solutions. + +=item C<S<< BisectionCutoff => 40 >>> + +maximum number of bisections to +perform when looking for a solution. + +=back + +You may set any of these using Context()->flags->set(...). + +In addition to the Context flags, you can set some values within +the ImplicitEquation itself: + +=over + +=item C<S<< tolerance >>> + +the absolute tolerance for zeros of the function. + +=item C<S<< bisect_tolerance >>> + +the tolerance used when searching for zeros. + +=item C<S<< point_tolerance >>> + +the absolute tolerance for how close to an +actual solution the located solution must be. + +=item C<S<< limits >>> + +the domain to use for the function; see the +documentation for the Formula object. + +=item C<S<< solutions >>> + +a reference to an array of references to arrays +that contain the coordinates of the points +that are the solutions of the equation. + +=back + +These can be set in the in the ImplicitEquation() call that creates +the object, as in the examples below: + +For example: + + $f = ImplicitEquation("x^2-y^2=0", + solutions => [[0,0],[1,1],[-1,1],[-1,-1],[1,-1]], + tolerance => .001 + ); + + + $f = ImplicitEquation("xy=5",limits=>[-3,3]); + +The limits value can be set globally within the Context, if you wish, +and the others can be controlled by the Context flags discussed +above. =cut +loadMacros("MathObjects.pl"); + +sub _parserImplicitEquation_init {ImplicitEquation::Init()}; # don't reload this file + # # Create the ImplicitEquation package # Index: problemPreserveAnswers.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/problemPreserveAnswers.pl,v retrieving revision 1.4 retrieving revision 1.5 diff -Lmacros/problemPreserveAnswers.pl -Lmacros/problemPreserveAnswers.pl -u -r1.4 -r1.5 --- macros/problemPreserveAnswers.pl +++ macros/problemPreserveAnswers.pl @@ -1,33 +1,32 @@ +=head1 NAME -sub _problemPreserveAnswers_init {PreserveAnswers::Init()} +problemPreserveAnswers.pl - Allow sticky answers to preserve special characters. + +=head1 DESCRIPTION + +This file implements a fragile hack to overcome a problem with +PGbasicmacros.pl, which removes special characters from student +answers (in order to prevent EV3 from mishandling them). + +Unfortunately, this means that "sticky" answers will lose +those characters, which makes it very difficult to answer +problems with more than one answer when the student wants +to submit several times while working on later parts. -=head1 PreserveAnswers(); +The real fix to to rewrite PGbasicmacros.pl to handle +this better, but this hack will handle the situation for +now until that can be accomplished. - ###################################################################### - # - # This file implements a fragile hack to overcome a problem with - # PGbasicmacros.pl, which removes special characters from student - # answers (in order to prevent EV3 from mishandling them). - # - # Unfortunately, this means that "sticky" answers will lose - # those characters, which makes it very difficult to answer - # problems with more than one answer when the student wants - # to submit several times while working on later parts. - # - # The real fix to to rewrite PGbasicmacros.pl to handle - # this better, but this hack will handle the situation for - # now until that can be accomplished. - # - # To use this hack, simply load the file using - # - # loadMacros("problemPreserveAnswers.pl"); - # - # at the top of your PG file. - # - ###################################################################### +To use this hack, simply load the file using + + loadMacros("problemPreserveAnswers.pl"); + +at the top of your PG file. =cut +sub _problemPreserveAnswers_init {PreserveAnswers::Init()} + package PreserveAnswers; # Index: parserParametricLine.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserParametricLine.pl,v retrieving revision 1.12 retrieving revision 1.13 diff -Lmacros/parserParametricLine.pl -Lmacros/parserParametricLine.pl -u -r1.12 -r1.13 --- macros/parserParametricLine.pl +++ macros/parserParametricLine.pl @@ -1,46 +1,47 @@ -loadMacros('MathObjects.pl'); +=head1 NAME -sub _parserParametricLine_init {ParametricLine::Init()}; # don't reload this file +parserParametricLine.pl - Implements Formulas that represent parametric lines. =head1 DESCRIPTION - ###################################################################### - # - # This is a Parser class that implements parametric lines as - # a subclass of the Formula class. The standard ->cmp routine - # will work for this, provided we define the compare() function - # needed by the overloaded ==. We assign the special precedence - # so that overloaded operations will be promoted to the ones below. - # - # Use ParametricLine(point,vector) or ParametricLine(formula) - # to create a ParametricLine object. You can pass an optional - # additional parameter that indicated the variable to use for the - # parameter for the line. - # - # Usage examples: - # - # $L = ParametricLine(Point(3,-1,2),Vector(1,1,3)); - # $L = ParametricLine([3,-1,2],[1,1,3]); - # $L = ParametricLine("<t,1-t,2t-3>"); - # - # $p = Point(3,-1,2); $v = Vector(1,1,3); - # $L = ParametricLine($p,$v); - # - # $t = Formula('t'); $p = Point(3,-1,2); $v = Vector(1,1,3); - # $L = ParametricLine($p+$t*$v); - # - # Context()->constants->are(a=>1+pi^2); # won't guess this value - # $L = ParametricLine("(a,2a,-1) + t <1,a,a^2>"); - # - # Then use - # - # ANS($L->cmp); - # - # to get the answer checker for $L. - # +This is a Parser class that implements parametric lines as +a subclass of the Formula class. The standard ->cmp routine +will work for this, provided we define the compare() function +needed by the overloaded ==. We assign the special precedence +so that overloaded operations will be promoted to the ones below. + +Use ParametricLine(point,vector) or ParametricLine(formula) +to create a ParametricLine object. You can pass an optional +additional parameter that indicated the variable to use for the +parameter for the line. + +Usage examples: + + $L = ParametricLine(Point(3,-1,2),Vector(1,1,3)); + $L = ParametricLine([3,-1,2],[1,1,3]); + $L = ParametricLine("<t,1-t,2t-3>"); + + $p = Point(3,-1,2); $v = Vector(1,1,3); + $L = ParametricLine($p,$v); + + $t = Formula('t'); $p = Point(3,-1,2); $v = Vector(1,1,3); + $L = ParametricLine($p+$t*$v); + + Context()->constants->are(a=>1+pi^2); # won't guess this value + $L = ParametricLine("(a,2a,-1) + t <1,a,a^2>"); + +Then use + + ANS($L->cmp); + +to get the answer checker for $L. =cut +loadMacros('MathObjects.pl'); + +sub _parserParametricLine_init {ParametricLine::Init()}; # don't reload this file + # # Define the subclass of Formula # Index: parserMultiPart.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserMultiPart.pl,v retrieving revision 1.10 retrieving revision 1.11 diff -Lmacros/parserMultiPart.pl -Lmacros/parserMultiPart.pl -u -r1.10 -r1.11 --- macros/parserMultiPart.pl +++ macros/parserMultiPart.pl @@ -1,17 +1,17 @@ -sub _parserMultiPart_init {} +=head1 NAME + +parserMultiPart.pl - [DEPRECATED] Renamed to MultiAnswer. -=head3 MultiPart +=head1 DESCRIPTION - ###################################################################### - # - # This object has been renamed MultiAnswer and is now available in - # parserMultiAnswer.pl. Using a MultiPart object will produce a - # warning to that effect. - # - ###################################################################### +This object has been renamed MultiAnswer and is now available in +parserMultiAnswer.pl. Using a MultiPart object will produce a +warning to that effect. =cut +sub _parserMultiPart_init {} + loadMacros("parserMultiAnswer.pl"); sub MultiPart { warn "The MultiPart object has been depricated.${BR}You should use MultiAnswer object instead"; Index: parserVectorUtils.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserVectorUtils.pl,v retrieving revision 1.5 retrieving revision 1.6 diff -Lmacros/parserVectorUtils.pl -Lmacros/parserVectorUtils.pl -u -r1.5 -r1.6 --- macros/parserVectorUtils.pl +++ macros/parserVectorUtils.pl @@ -1,27 +1,27 @@ +=head1 NAME + +parserVectorUtils.pl - Utility macros that are useful in vector problems. =head1 DESCRIPTION - ##################################################################### - # - # Some utility routines that are useful in vector problems - # +Some utility routines that are useful in vector problems =cut sub _parserVectorUtils_init {}; # don't reload this file -=head3 Overline($vectorName) +=head1 MACROS + +=head2 Overline + + Overline($vectorName) - ################################################## +formats a vector name (should be used in math mode) - # - # formats a vector name (should be used in math mode) - # - # Vectors will be in bold italics in HTML modes, and - # will be overlined in TeX modes. (Bold italic could also work in - # TeX modes, but the low resolution on screen made it less easy - # to distinguish the difference between bold and regular letters.) - # +Vectors will be in bold italics in HTML modes, and +will be overlined in TeX modes. (Bold italic could also work in +TeX modes, but the low resolution on screen made it less easy +to distinguish the difference between bold and regular letters.) =cut @@ -36,15 +36,15 @@ ); } -=head3 BoldMath($vectorName) +=head2 BoldMath - # - # This gets a bold letter in TeX as well as HTML modes. - # Although \boldsymbol{} works fine on screen in latex2html mode, - # the PDF file produces non-bold letters. I haven't been able to - # track this down, so used \mathbf{} in TeX mode, which produces - # roman bold, not math-italic bold. - # + BoldMath($vectorName) + +This gets a bold letter in TeX as well as HTML modes. +Although \boldsymbol{} works fine on screen in latex2html mode, +the PDF file produces non-bold letters. I haven't been able to +track this down, so used \mathbf{} in TeX mode, which produces +roman bold, not math-italic bold. =cut @@ -61,28 +61,32 @@ ); } -=head3 $GRAD +=head2 $GRAD + + TEXT($GRAD) - # - # Grad symbol - # + BEGIN_TEXT + $GRAD + END_TEXT + +Grad symbol. =cut $GRAD = '\nabla '; -=head3 non_zero_point($Dim,$L_bound,$U_bound,$step) +=head2 non_zero_point + + non_zero_point($Dim,$L_bound,$U_bound,$step) + +Create a non-zero point with the given number of coordinates +with the given random range (which defaults to (-5,5,1)). + +non_zero_point(n,a,b,c) +non_zero_point_2D(a,b,c) +non_zero_point_3D(a,b,c) - # - # Create a non-zero point with the given number of coordinates - # with the given random range (which defaults to (-5,5,1)). - # - # non_zero_point(n,a,b,c) - # non_zero_point_2D(a,b,c) - # non_zero_point_3D(a,b,c) - # - # non_zero_point2D and 3D automatically set Dimension to 2 and 3 respectively. - # +non_zero_point2D and 3D automatically set Dimension to 2 and 3 respectively. =cut @@ -96,13 +100,16 @@ sub non_zero_point2D {non_zero_point(2,@_)} sub non_zero_point3D {non_zero_point(3,@_)} -=head3 non_zero_vector($Dim,$L_bound,$U_bound,$step) +=head2 non_zero_vector, non_zero_vector2D, non_zero_vector3D - # - # Functions the same as non_zero_point but for Vectors - # - # non_zero_vector2D and 3D automatically set Dimension to 2 and 3 respectively. - # + non_zero_vector($Dim,$L_bound,$U_bound,$step) + + non_zero_vector2D($L_bound,$U_bound,$step) + + non_zero_vector3D($L_bound,$U_bound,$step) + +Functions the same as non_zero_point but for Vectors. non_zero_vector2D and +non_zero_vector3D automatically set Dimension to 2 and 3 respectively. =cut @@ -110,22 +117,25 @@ sub non_zero_vector2D {non_zero_vector(2,@_)} sub non_zero_vector3D {non_zero_vector(3,@_)} -=head3 Line(Point(@coords1),Vector(@coords2),'variableLetter') +=head2 Line + + $P = Point(@coords1); + $V = Vector(@coords2); + $t = 't'; + Line($P,$V); + Line($P,$V,$t); - # - # Form the vector-parametric form for a line given its point and vector - # - # Usage: Line(P,V); or Line(P,V,'t'); - # - # where P is the point and V the direction vector for the line, and - # t is the variable to use (default is 't'). - # - # Ex: Line([1,-3],[2,1]) produces Vector("1+2t","-3+t"). - # Ex: Line(Point(1,-3),Vector(2,1)) produces Vector("1+2t","-3+t"). - # - # (It may be better to use the ParametricLine class from - # parserParametricLine.pl). - # +Form the vector-parametric form for a line given its point and vector, where $P +is the point and $V the direction vector for the line, and $t is the variable to +use (default is 't'). + +For example: + + Line([1,-3],[2,1]); # produces Vector("1+2t","-3+t"). + Line(Point(1,-3),Vector(2,1)); # produces Vector("1+2t","-3+t"). + +(It may be better to use the ParametricLine class from +parserParametricLine.pl). =cut @@ -138,15 +148,13 @@ return Vector(@coords); } -=head3 Plane($point,$NormalVector) +=head2 Plane + + Plane($point,$NormalVector) - # - # Creates a displayable string for a plane given its - # normal vector and a point on the plane. (Better to use - # the ImplicitPlane class from parserImplicitPlane.pl). - # - # Usage: Plane(P,N); - # +Creates a displayable string for a plane given its +normal vector and a point on the plane. (Better to use +the ImplicitPlane class from parserImplicitPlane.pl). =cut Index: parserImplicitPlane.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserImplicitPlane.pl,v retrieving revision 1.16 retrieving revision 1.17 diff -Lmacros/parserImplicitPlane.pl -Lmacros/parserImplicitPlane.pl -u -r1.16 -r1.17 --- macros/parserImplicitPlane.pl +++ macros/parserImplicitPlane.pl @@ -1,55 +1,54 @@ -loadMacros('MathObjects.pl'); +=head1 NAME -sub _parserImplicitPlane_init {ImplicitPlane::Init()}; # don't reload this file +parserImplicitPlane.pl - Implement implicit planes. =head1 DESCRIPTION - ###################################################################### - # - # This is a Parser class that implements implicit planes as - # a subclass of the Formula class. The standard ->cmp routine - # will work for this, provided we define the compare() function - # needed by the overloaded ==. We assign the special precedence - # so that overloaded operations will be promoted to the ones below. - # - # - # Use ImplicitPlane(point,vector), ImplicitPlane(point,number) or - # ImplicitPlane(formula) to create an ImplicitPlane object. - # The first form uses the point as a point on the plane and the - # vector as the normal for the plane. The second form uses the point - # as the coefficients of the variables and the number as the value - # that the formula must equal. The third form uses the formula - # directly. - # - # The number of variables in the Context determines the dimension of - # the "plane" being defined. If there are only two, the formula - # produces an implicit line, but if there are four variables, it will - # be a hyperplane in four-space. You can specify the variables you - # want to use by supplying an additional parameter, which is a - # reference to an array of variable names. - # - # - # Usage examples: - # - # $P = ImplicitPlane(Point(1,0,2),Vector(-1,1,3)); # -x+y+3z = 5 - # $P = ImplicitPlane([1,0,2],[-1,1,3]); # -x+y+3z = 5 - # $P = ImplicitPlane([1,0,2],4); # x+2z = 4 - # $P = ImplicitPlane("x+2y-z=5"); - # - # Context()->variables->are(x=>'Real',y=>'Real',z=>'Real',w=>'Real'); - # $P = ImplicitPlane([1,0,2,-1],10); # w+2y-z = 10 (alphabetical order) - # $P = ImplicitPlane([3,-1,2,4],5,['x','y','z','w']); # 3x-y+2z+4w = 5 - # $P = ImplicitPlane([3,-1,2],5,['y','z','w']); # 3y-z+2w = 5 - # - # Then use - # - # ANS($P->cmp); - # - # to get the answer checker for $P. - # +This is a Parser class that implements implicit planes as +a subclass of the Formula class. The standard ->cmp routine +will work for this, provided we define the compare() function +needed by the overloaded ==. We assign the special precedence +so that overloaded operations will be promoted to the ones below. + +Use ImplicitPlane(point,vector), ImplicitPlane(point,number) or +ImplicitPlane(formula) to create an ImplicitPlane object. +The first form uses the point as a point on the plane and the +vector as the normal for the plane. The second form uses the point +as the coefficients of the variables and the number as the value +that the formula must equal. The third form uses the formula +directly. + +The number of variables in the Context determines the dimension of +the "plane" being defined. If there are only two, the formula +produces an implicit line, but if there are four variables, it will +be a hyperplane in four-space. You can specify the variables you +want to use by supplying an additional parameter, which is a +reference to an array of variable names. + +Usage examples: + + $P = ImplicitPlane(Point(1,0,2),Vector(-1,1,3)); # -x+y+3z = 5 + $P = ImplicitPlane([1,0,2],[-1,1,3]); # -x+y+3z = 5 + $P = ImplicitPlane([1,0,2],4); # x+2z = 4 + $P = ImplicitPlane("x+2y-z=5"); + + Context()->variables->are(x=>'Real',y=>'Real',z=>'Real',w=>'Real'); + $P = ImplicitPlane([1,0,2,-1],10); # w+2y-z = 10 (alphabetical order) + $P = ImplicitPlane([3,-1,2,4],5,['x','y','z','w']); # 3x-y+2z+4w = 5 + $P = ImplicitPlane([3,-1,2],5,['y','z','w']); # 3y-z+2w = 5 + +Then use + + ANS($P->cmp); + +to get the answer checker for $P. =cut +loadMacros('MathObjects.pl'); + +sub _parserImplicitPlane_init {ImplicitPlane::Init()}; # don't reload this file + ################################################## # # Define the subclass of Formula Index: problemRandomize.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/problemRandomize.pl,v retrieving revision 1.8 retrieving revision 1.9 diff -Lmacros/problemRandomize.pl -Lmacros/problemRandomize.pl -u -r1.8 -r1.9 --- macros/problemRandomize.pl +++ macros/problemRandomize.pl @@ -1,98 +1,127 @@ +=head1 NAME -=head1 ProblemRandomize(); +problemRandomize.pl - Reseed a problem so that students can do additional versions for +more practice. - ###################################################################### - # - # This file implements a mechanism for allowing a problem file to be - # "reseeded" so that the student can do additional versions of the - # problem. You can control when the reseed message is available, - # and what style to use for it. - # - # To use the problemRandimize library, use - # - # loadMacros("problemRandomize.pl"); - # - # at the top of your problem file, and then create a problemRandomize - # object with - # - # $pr = ProblemRandomize(options); - # - # where '$pr' is the name of the variable you will use to refer - # to the randomized problem (if needed), and 'options' can include: - # - # when => type Specifies the condition on which - # reseeding the problem is allowed. - # The choices include: - # - # "Correct" (only when the problem has - # been answered correctly.) - # - # "Always" (reseeding is always allowed.) - # - # Default: "Correct" - # - # onlyAfterDue => 0 or 1 Specifies if the reseed option is only - # allowed after the due date has passed. - # Default: 1 - # - # style => type Determines the type of interaction needed - # to reseed the problem. Types include: - # - # "Button" (a button) - # - # "Checkbox" (a checkbox plus pressing submit) - # - # "Input" (an input box where the seed - # can be set explicitly) - # - # "HTML" (the HTML is given explicitly - # via the "label" option below) - # - # Default: "Button" - # - # label => "text" Specifies the text used for the button name, - # checkbox label, input box label, or raw HTML - # used for the reseed mechanism. - # - # The problemRandomize library installs a special grader that handles determining - # when the reseed option will be available. It also redefines install_problem_grader - # so that it will not overwrite the one installed by the library (it is stored so - # that it can be called internally by the problemRandomize library's grader). - # - # Note that the problem will store the new problem seed only if the student can - # submit saved answers (i.e., only before the due date). After the due date, - # the student can get new versions, but the problem will revert to the original - # version when they come back to the problem later. Since the default is only - # to allow reseeding afer the due date, the reseeding will not be sticky by default. - # Hardcopy ALWAYS produces the original version of the problem, regardless of - # the seed saved by the student. - # - # Examples: - # - # ProblemRandomize(); # use all defaults - # ProblemRandomize(when=>"Always"); # always can reseed (after due date) - # ProblemRandomize(onlyAfterDue=>0); # can reseed whenever correct - # ProblemRandomize(when=>"always",onlyAfterDue=>0); # always can reseed - # - # ProblemRandomize(style=>"Input"); # use an input box to set the seed - # - # For problems that include "PGcourse.pl" in their loadMacros() calls, you can - # use that file to provide reseed buttons for ALL problems simply by including - # - # loadMacros("problemRandomize.pl"); - # ProblemRandomize(); - # - # in PGcourse.pl. You can make the ProblemRandomize() be dependent on the set - # number or the set or the login ID or whatever. For example - # - # loadMacros("problemRandomize.pl"); - # ProblemRandomize(when=>"always",onlyAfterDue=>0,style=>"Input") - # if $studentLogin eq "dpvc"; - # - # would enable reseeding at any time for the user called "dpvc" (presumably a - # professor). You can test $probNum and $setNumber to make reseeding available - # only for specific sets or problems within a set. - # +=head1 DESCRIPTION + +This file implements a mechanism for allowing a problem file to be +"reseeded" so that the student can do additional versions of the +problem. You can control when the reseed message is available, +and what style to use for it. + +To use the problemRandimize library, use + + loadMacros("problemRandomize.pl"); + +at the top of your problem file, and then create a problemRandomize +object with + + $pr = ProblemRandomize(options); + +where '$pr' is the name of the variable you will use to refer +to the randomized problem (if needed), and 'options' can include: + +=over + +=item C<S<< when => type >>> + +Specifies the condition on which +reseeding the problem is allowed. +The choices include: + +=over + +=item * + +C<Correct> - only when the problem has been answered correctly. + +=item * + +C<Always> - reseeding is always allowed. + +=back + +Default: "Correct" + +=item C<S<< onlyAfterDue => 0 or 1 >>> + +Specifies if the reseed option is only +allowed after the due date has passed. +Default: 1 + +=item C<S<< style => type >>> + +Determines the type of interaction needed +to reseed the problem. Types include: + +=over + +=item * + +C<Button> - a button. + +=item * + +C<Checkbox> - a checkbox plus pressing submit. + +=item * + +C<Input> - an input box where the seed can be set explicitly. + +=item * + +C<HTML> - the HTML is given explicitly via the "label" option below. + +=back + +Default: "Button" + +=item C<S<< label => "text" >>> + +Specifies the text used for the button name, +checkbox label, input box label, or raw HTML +used for the reseed mechanism. + +=back + +The problemRandomize library installs a special grader that handles determining +when the reseed option will be available. It also redefines install_problem_grader +so that it will not overwrite the one installed by the library (it is stored so +that it can be called internally by the problemRandomize library's grader). + +Note that the problem will store the new problem seed only if the student can +submit saved answers (i.e., only before the due date). After the due date, +the student can get new versions, but the problem will revert to the original +version when they come back to the problem later. Since the default is only +to allow reseeding afer the due date, the reseeding will not be sticky by default. +Hardcopy ALWAYS produces the original version of the problem, regardless of +the seed saved by the student. + +Examples: + + ProblemRandomize(); # use all defaults + ProblemRandomize(when=>"Always"); # always can reseed (after due date) + ProblemRandomize(onlyAfterDue=>0); # can reseed whenever correct + ProblemRandomize(when=>"always",onlyAfterDue=>0); # always can reseed + ProblemRandomize(style=>"Input"); # use an input box to set the seed + +For problems that include "PGcourse.pl" in their loadMacros() calls, you can +use that file to provide reseed buttons for ALL problems simply by including + + loadMacros("problemRandomize.pl"); + ProblemRandomize(); + +in PGcourse.pl. You can make the ProblemRandomize() be dependent on the set +number or the set or the login ID or whatever. For example + + loadMacros("problemRandomize.pl"); + ProblemRandomize(when=>"always",onlyAfterDue=>0,style=>"Input") + if $studentLogin eq "dpvc"; + +would enable reseeding at any time for the user called "dpvc" (presumably a +professor). You can test $probNum and $setNumber to make reseeding available +only for specific sets or problems within a set. =cut Index: parserNumberWithUnits.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserNumberWithUnits.pl,v retrieving revision 1.6 retrieving revision 1.7 diff -Lmacros/parserNumberWithUnits.pl -Lmacros/parserNumberWithUnits.pl -u -r1.6 -r1.7 --- macros/parserNumberWithUnits.pl +++ macros/parserNumberWithUnits.pl @@ -1,31 +1,30 @@ -loadMacros('MathObjects.pl'); +=head1 NAME + +parserNumberWithUnits.pl - Implements a number with units. =head1 DESCRIPTION - ###################################################################### - # - # This is a Parser class that implements a number with units. - # It is a temporary version until the Parser can handle it - # directly. - # - # Use NumberWithUnits("num units") or NumberWithUnits(formula,"units") - # to generate a NumberWithUnits object, and then call its cmp method - # to get an answer checker for your number with units. - # - # Usage examples: - # - # ANS(NumberWithUnits("3 ft")->cmp); - # ANS(NumberWithUnits("$a*$b ft")->cmp); - # ANS(NumberWithUnits($a*$b,"ft")->cmp); - # - - # - # We now call on the Legacy version, which is used by - # num_cmp to handle numbers with units. - # +This is a Parser class that implements a number with units. +It is a temporary version until the Parser can handle it +directly. + +Use NumberWithUnits("num units") or NumberWithUnits(formula,"units") +to generate a NumberWithUnits object, and then call its cmp method +to get an answer checker for your number with units. + +Usage examples: + + ANS(NumberWithUnits("3 ft")->cmp); + ANS(NumberWithUnits("$a*$b ft")->cmp); + ANS(NumberWithUnits($a*$b,"ft")->cmp); + +We now call on the Legacy version, which is used by +num_cmp to handle numbers with units. =cut +loadMacros('MathObjects.pl'); + sub _parserNumberWithUnits_init { main::PG_restricted_eval('sub NumberWithUnits {Parser::Legacy::NumberWithUnits->new(@_)}'); } Index: parserPopUp.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserPopUp.pl,v retrieving revision 1.5 retrieving revision 1.6 diff -Lmacros/parserPopUp.pl -Lmacros/parserPopUp.pl -u -r1.5 -r1.6 --- macros/parserPopUp.pl +++ macros/parserPopUp.pl @@ -1,41 +1,42 @@ -loadMacros('MathObjects.pl','contextString.pl'); +=head1 NAME -sub _parserPopUp_init {parserPopUp::Init()}; # don't reload this file +parserPopUp.pl - Pop-up menus compatible with Value objects. =head1 DESCRIPTION - #################################################################### - # - # This file implements a pop-up menu object that is compatible - # with Value objects, and in particular, with the MultiPart object. - # - # To create a PopUp object, use - # - # $popup = PopUp([choices,...],correct); - # - # where "choices" are the strings for the items in the popup menu, - # and "correct" is the choice that is the correct answer for the - # popup. - # - # To insert the popup menu into the problem text, use - # - # BEGIN_TEXT - # \{$popup->menu\} - # END_TEXT - # - # and then - # - # ANS($popup->cmp); - # - # to get the answer checker for the popup. - # - # You can use the PopUp menu object in MultiPart objects. This is - # the reason for the pop-up menu's ans_rule method (since that is what - # MultiPart calls to get answer rules). - # +This file implements a pop-up menu object that is compatible +with Value objects, and in particular, with the MultiAnswer object. + +To create a PopUp object, use + + $popup = PopUp([choices,...],correct); + +where "choices" are the strings for the items in the popup menu, +and "correct" is the choice that is the correct answer for the +popup. + +To insert the popup menu into the problem text, use + + BEGIN_TEXT + \{$popup->menu\} + END_TEXT + +and then + + ANS($popup->cmp); + +to get the answer checker for the popup. + +You can use the PopUp menu object in MultiAnswer objects. This is +the reason for the pop-up menu's ans_rule method (since that is what +MultiAnswer calls to get answer rules). =cut +loadMacros('MathObjects.pl','contextString.pl'); + +sub _parserPopUp_init {parserPopUp::Init()}; # don't reload this file + # # The package that implements pop-up menus # Index: parserSolutionFor.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserSolutionFor.pl,v retrieving revision 1.6 retrieving revision 1.7 diff -Lmacros/parserSolutionFor.pl -Lmacros/parserSolutionFor.pl -u -r1.6 -r1.7 --- macros/parserSolutionFor.pl +++ macros/parserSolutionFor.pl @@ -1,60 +1,61 @@ -loadMacros("MathObjects.pl"); +=head1 NAME -sub _parserSolutionFor_init {}; # don't reload this file +parserSolutionFor.pl - An answer checker that checks if a student's answer +satisifies an implicit equation. =head1 DESCRIPTION - ###################################################################### - # - # This is a Parser class that implements an answer checker that - # checks if a student's answer satisfies an implicit equation. - # We define a SolutionFor object class that lets you specify an - # equality that the student answer must satisfy, and a point that - # DOES satify the equation. The overloaded == operator will - # check if a given point satisfies the given equality. - # - # Use SolutionFor(equality,point[,options]) to create a SolutionFor object. - # The equality is a Formula object containing an equality, or a string - # representing such a formula, and the point is a Point object or string - # containing a point that satisfies the equation (to be used as the - # correct answer when the student asks to see the answers). - # - # The variables to use are declared in the Context in the usual way, - # and the coordinates of the student point will be considered to be in - # alphabetical order. You can override this by supplying the vars=>[...] - # option, where you specify the variable names in the order you want the - # student to give them. E.g., vars=>['y','x'] will make the student answer - # represent the point (y,x) rather than the default (x,y). - # - # Usage examples: - # - # Context("Vector")->variables->are(x=>'Real',y=>'Real'); - # $f = SolutionFor("x^2 = cos(y)","(1,0)"); - # $f = SolutionFor("x^2 - y = 0",[2,4]); - # $f = SolutionFor("x^2 - y = 0",Point(4,2),vars=>['y','x']); - # - # Then use - # - # ANS($f->cmp); - # - # to get the answer checker for $f. - # - # You can use $f->{f} to get the Formula object for the equality used - # in the object, and $f->f(point) to determine if the given point is - # a solution to the equality or not. For example, if you want to include - # the TeX version of a formula within the text of a problem, you can use: - # - # Context()->texStrings; - # BEGIN_TEXT - # A solution to \($f->{f}\) is \((x,y)\) = \{ans_rule(30)\}. - # END_TEXT - # Context()->normalStrings; - # ANS($f->cmp); - # - ###################################################################### +This is a Parser class that implements an answer checker that +checks if a student's answer satisfies an implicit equation. +We define a SolutionFor object class that lets you specify an +equality that the student answer must satisfy, and a point that +DOES satify the equation. The overloaded == operator will +check if a given point satisfies the given equality. + +Use SolutionFor(equality,point[,options]) to create a SolutionFor object. +The equality is a Formula object containing an equality, or a string +representing such a formula, and the point is a Point object or string +containing a point that satisfies the equation (to be used as the +correct answer when the student asks to see the answers). + +The variables to use are declared in the Context in the usual way, +and the coordinates of the student point will be considered to be in +alphabetical order. You can override this by supplying the vars=>[...] +option, where you specify the variable names in the order you want the +student to give them. E.g., vars=>['y','x'] will make the student answer +represent the point (y,x) rather than the default (x,y). + +Usage examples: + + Context("Vector")->variables->are(x=>'Real',y=>'Real'); + $f = SolutionFor("x^2 = cos(y)","(1,0)"); + $f = SolutionFor("x^2 - y = 0",[2,4]); + $f = SolutionFor("x^2 - y = 0",Point(4,2),vars=>['y','x']); + +Then use + + ANS($f->cmp); + +to get the answer checker for $f. + +You can use $f->{f} to get the Formula object for the equality used +in the object, and $f->f(point) to determine if the given point is +a solution to the equality or not. For example, if you want to include +the TeX version of a formula within the text of a problem, you can use: + + Context()->texStrings; + BEGIN_TEXT + A solution to \($f->{f}\) is \((x,y)\) = \{ans_rule(30)\}. + END_TEXT + Context()->normalStrings; + ANS($f->cmp); =cut +loadMacros("MathObjects.pl"); + +sub _parserSolutionFor_init {}; # don't reload this file + # # Create a SolutionFor object of the correct type # Index: parserMultiAnswer.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserMultiAnswer.pl,v retrieving revision 1.6 retrieving revision 1.7 diff -Lmacros/parserMultiAnswer.pl -Lmacros/parserMultiAnswer.pl -u -r1.6 -r1.7 --- macros/parserMultiAnswer.pl +++ macros/parserMultiAnswer.pl @@ -1,90 +1,84 @@ -loadMacros("MathObjects.pl"); +=head1 NAME -=head3 MultiAnswer +parserMultiAnswer.pl - Tie several blanks to a single answer checker. - ###################################################################### - # - # MultiAnswer 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 MultiAnswer can produce either a single - # result in the answer results area, or a separate result for each - # blank. - # - # To create a MultiAnswer pass a list of answers to MultiAnswer() in the - # order they will appear in the problem. For example: - # - # $mp = MultiAnswer("x^2",-1,1); - # - # or - # - # $mp = MultiAnswer(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 MultiAnswer. - # 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 have to worry about handling bad data from the student - # (at least as far as typechecking goes). - # - # The checker routine should accept four parameters: a reference to the array - # of correct answers, a reference to the array of student answers, a reference - # to the MultiAnswer itself, and a reference to the answer hash. It should do - # whatever checking it needs to do and then return a score for the MultiAnswer - # 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 MultiAnswer'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 MultiAnswer), or can - # call Value::Error() to generate an error and die. - # - # The checker routine can be supplied either when the MultiAnswer is created, or - # when the cmp() method is called. For example: - # - # $mp = MultiAnswer("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 = MultiAnswer("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)); - # - ###################################################################### +=head1 DESCRIPTION + +MultiAnswer 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 MultiAnswer can produce either a single +result in the answer results area, or a separate result for each +blank. + +To create a MultiAnswer pass a list of answers to MultiAnswer() in the +order they will appear in the problem. For example: + + $mp = MultiAnswer("x^2",-1,1); + +or + + $mp = MultiAnswer(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 MultiAnswer. +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 have to worry about handling bad data from the student +(at least as far as typechecking goes). + +The checker routine should accept four parameters: a reference to the array +of correct answers, a reference to the array of student answers, a reference +to the MultiAnswer itself, and a reference to the answer hash. It should do +whatever checking it needs to do and then return a score for the MultiAnswer +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 MultiAnswer'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 MultiAnswer), or can +call Value::Error() to generate an error and die. + +The checker routine can be supplied either when the MultiAnswer is created, or +when the cmp() method is called. For example: + + $mp = MultiAnswer("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 = MultiAnswer("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)); =cut +loadMacros("MathObjects.pl"); + sub _parserMultiAnswer_init { main::PG_restricted_eval('sub MultiAnswer {MultiAnswer->new(@_)}'); } |