From: Mike G. v. a. <we...@ma...> - 2008-06-24 01:02:15
|
Log Message: ----------- merging with HEAD 6/23/2008 see Value.pm for list of significant changes Tags: ---- rel-2-4-dev Modified Files: -------------- pg/macros: IO.pl LinearProgramming.pl MathObjects.pl PG.pl PGanswermacros.pl PGasu.pl PGauxiliaryFunctions.pl PGbasicmacros.pl PGchoicemacros.pl PGcomplexmacros.pl PGgraphmacros.pl PGinfo.pl Parser.pl Value.pl answerComposition.pl answerCustom.pl answerVariableList.pl contextABCD.pl contextIntegerFunctions.pl contextLimitedComplex.pl contextLimitedNumeric.pl contextLimitedPoint.pl contextLimitedPolynomial.pl contextLimitedPowers.pl contextLimitedVector.pl contextPeriodic.pl contextString.pl contextTF.pl dangerousMacros.pl displayMacros.pl extraAnswerEvaluators.pl parserCustomization.pl parserDifferenceQuotient.pl parserFormulaWithUnits.pl parserFunction.pl parserImplicitEquation.pl parserImplicitPlane.pl parserMultiAnswer.pl parserMultiPart.pl parserNumberWithUnits.pl parserParametricLine.pl parserPopUp.pl parserRadioButtons.pl parserSolutionFor.pl parserVectorUtils.pl Added Files: ----------- pg/macros: AppletObjects.pl PGcourse.pl PGfunctionevaluators.pl PGmiscevaluators.pl PGnumericevaluators.pl PGstringevaluators.pl PGtextevaluators.pl answerHints.pl compoundProblem.pl contextCurrency.pl contextInequalities.pl contextPiecewiseFunction.pl contextScientificNotation.pl parserAssignment.pl parserAutoStrings.pl parserFormulaUpToConstant.pl problemPreserveAnswers.pl problemRandomize.pl Revision Data ------------- Index: parserParametricLine.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserParametricLine.pl,v retrieving revision 1.5.2.1 retrieving revision 1.5.2.2 diff -Lmacros/parserParametricLine.pl -Lmacros/parserParametricLine.pl -u -r1.5.2.1 -r1.5.2.2 --- macros/parserParametricLine.pl +++ macros/parserParametricLine.pl @@ -1,63 +1,63 @@ -loadMacros('Parser.pl'); +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader$ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ -sub _parserParametricLine_init {}; # don't reload this file +=head1 NAME + + +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 -# -# Define a new context for lines -# -$context{ParametricLine} = Parser::Context->getCopy(undef,"Vector"); -$context{ParametricLine}->variables->are(t=>'Real'); -$context{ParametricLine}->{precedence}{ParametricLine} = - $context{ParametricLine}->{precedence}{special}; -$context{ParametricLine}->reduction->set('(-x)-y'=>0); -# -# Make it active -# -Context("ParametricLine"); +loadMacros('MathObjects.pl'); -# -# Syntactic sugar -# -sub ParametricLine {ParametricLine->new(@_)} +sub _parserParametricLine_init {ParametricLine::Init()}; # don't reload this file # # Define the subclass of Formula @@ -65,6 +65,18 @@ package ParametricLine; our @ISA = qw(Value::Formula); +sub Init { + my $context = $main::context{ParametricLine} = Parser::Context->getCopy("Vector"); + $context->{name} = "ParametricLine"; + $context->variables->are(t=>'Real'); + $context->{precedence}{ParametricLine} = $context->{precedence}{special}; + $context->reduction->set('(-x)-y'=>0); + + main::Context("ParametricLine"); ### FIXME: probably should require author to set this explicitly + + main::PG_restricted_eval('sub ParametricLine {ParametricLine->new(@_)}'); +} + sub new { my $self = shift; my $class = ref($self) || $self; my $context = (Value::isContext($_[0]) ? shift : $self->context); @@ -95,14 +107,15 @@ return bless $line, $class; } -=head3 compare($lhs,$rhs) -# -# Two parametric lines are equal if they have -# parallel direction vectors and either the same -# points or the vector between the points is -# parallel to the (common) direction vector. -# +=head2 $lhs == $rhs + + # + # Two parametric lines are equal if they have + # parallel direction vectors and either the same + # points or the vector between the points is + # parallel to the (common) direction vector. + # =cut @@ -129,7 +142,7 @@ # sub cmp_postprocess { my $self = shift; my $ans = shift; - my $error = $sef->context->{error}{message}; + my $error = $self->context->{error}{message}; $self->cmp_error($ans) if $error =~ m/^(Your formula (isn't linear|doesn't look)|A line can't|The direction vector)/; } Index: contextLimitedPoint.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextLimitedPoint.pl,v retrieving revision 1.2.6.1 retrieving revision 1.2.6.2 diff -Lmacros/contextLimitedPoint.pl -Lmacros/contextLimitedPoint.pl -u -r1.2.6.1 -r1.2.6.2 --- macros/contextLimitedPoint.pl +++ macros/contextLimitedPoint.pl @@ -1,20 +1,42 @@ -loadMacros("Parser.pl"); +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader$ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +=head1 NAME + +contextLimitedPoint.pl - Allow point entry but no point operations. + +=head1 DESCRIPTION + +Implements a context in which points can be entered, +but no operations are permitted between points. So +students will be able to perform operations within the +coordinates of the points, but not between points. -sub _contextLimitedPoint_init {}; # don't load it again + Context("LimitedPoint") -=head3 Context("LimitedPoint") +=cut -########################################################## -# -# Implements a context in which points can be entered, -# but no operations are permitted between points. So -# students will be able to perform operations within the -# coordinates of the points, but not between points. -# -=cut +loadMacros("MathObjects.pl"); + +sub _contextLimitedPoint_init {LimitedPoint::Init()}; # don't load it again + +################################################## # # Handle common checking for BOPs # @@ -106,41 +128,46 @@ ############################################## ############################################## -package main; +package LimitedPoint; -# -# Now build the new context that calls the -# above classes rather than the usual ones -# +sub Init { + # + # Build the new context that calls the + # above classes rather than the usual ones + # + + my $context = $main::context{LimitedPoint} = Parser::Context->getCopy("Point"); + $context->{name} = "LimitedPoint"; + $context->operators->set( + '+' => {class => 'LimitedPoint::BOP::add'}, + '-' => {class => 'LimitedPoint::BOP::subtract'}, + '*' => {class => 'LimitedPoint::BOP::multiply'}, + '* ' => {class => 'LimitedPoint::BOP::multiply'}, + ' *' => {class => 'LimitedPoint::BOP::multiply'}, + ' ' => {class => 'LimitedPoint::BOP::multiply'}, + '/' => {class => 'LimitedPoint::BOP::divide'}, + ' /' => {class => 'LimitedPoint::BOP::divide'}, + '/ ' => {class => 'LimitedPoint::BOP::divide'}, + 'u+' => {class => 'LimitedPoint::UOP::plus'}, + 'u-' => {class => 'LimitedPoint::UOP::minus'}, + ); + # + # Remove these operators and functions + # + $context->operators->undefine('_','U','><','.'); + $context->functions->undefine('norm','unit'); + $context->lists->set( + AbsoluteValue => {class => 'LimitedPoint::List::AbsoluteValue'}, + ); + $context->parens->set( + '(' => {formMatrix => 0}, + '[' => {formMatrix => 0}, + ); + $context->variables->are(x=>'Real'); + $context->constants->remove('i','j','k'); + + main::Context("LimitedPoint"); ### FIXME: probably should require author to set this explicitly +} -$context{LimitedPoint} = Parser::Context->getCopy(undef,"Point"); -$context{LimitedPoint}->operators->set( - '+' => {class => 'LimitedPoint::BOP::add'}, - '-' => {class => 'LimitedPoint::BOP::subtract'}, - '*' => {class => 'LimitedPoint::BOP::multiply'}, - '* ' => {class => 'LimitedPoint::BOP::multiply'}, - ' *' => {class => 'LimitedPoint::BOP::multiply'}, - ' ' => {class => 'LimitedPoint::BOP::multiply'}, - '/' => {class => 'LimitedPoint::BOP::divide'}, - ' /' => {class => 'LimitedPoint::BOP::divide'}, - '/ ' => {class => 'LimitedPoint::BOP::divide'}, - 'u+' => {class => 'LimitedPoint::UOP::plus'}, - 'u-' => {class => 'LimitedPoint::UOP::minus'}, -); -# -# Remove these operators and functions -# -$context{LimitedPoint}->operators->undefine('_','U','><','.'); -$context{LimitedPoint}->functions->undefine('norm','unit'); -$context{LimitedPoint}->lists->set( - AbsoluteValue => {class => 'LimitedPoint::List::AbsoluteValue'}, -); -$context{LimitedPoint}->parens->set( - '(' => {formMatrix => 0}, - '[' => {formMatrix => 0}, -); -$context{LimitedPoint}->parens->remove('<'); -$context{LimitedPoint}->variables->are(x=>'Real'); -$context{LimitedPoint}->constants->remove('i','j','k'); -Context("LimitedPoint"); +1; Index: IO.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/IO.pl,v retrieving revision 1.3.6.1 retrieving revision 1.3.6.2 diff -Lmacros/IO.pl -Lmacros/IO.pl -u -r1.3.6.1 -r1.3.6.2 --- macros/IO.pl +++ macros/IO.pl @@ -1,19 +1,35 @@ ################################################################################ -# WeBWorK mod-perl (c) 2000-2002 WeBWorK Project -# $Id$ + +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader$ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. ################################################################################ =head1 NAME -IO.pl - Temporary location for IO functions that need access to the problem -environment. Formerly defined in IO.pm +IO.pl - Input/optput macros that require access to the problem environment. + +=head1 DESCRIPTION -See notes in Translator.pm +See notes in L<WeBWorK::PG::Translator>. =cut +# ^function _IO_init sub _IO_init {} + +# ^function _IO_export sub _IO_export { return ( '&send_mail_to', @@ -22,7 +38,25 @@ ); } +=head1 MACROS + +=head2 [DEPRECATED] send_mail_to + + send_mail_to($address, subject=>$subject, body=>$body) + +Send an email message with the subject $subject and body $body to the address +$address. This used to be used by mail_answers_to in PGbasicmacros.pl, but it no +longer is. Don't use this, I tell yah! + +=cut + # send_mail_to($user_address,'subject'=>$subject,'body'=>$body) +# ^function send_mail_to +# ^uses $envir{mailSmtpServer} +# ^uses $envir{mailSmtpSender} +# ^uses $REMOTE_HOST +# ^uses $REMOTE_ADDR +# ^uses Net::SMTP::new sub send_mail_to { my $user_address = shift; # user must be an instructor my %options = @_; @@ -80,34 +114,38 @@ return $out; } -sub getCourseTempDirectory { - return $envir{tempDirectory}; -} +=head2 getCourseTempDirectory -=head2 surePathToTmpFile + $path = getCourseTempDirectory() - surePathToTmpFile($path) - Returns: $path +Returns the path to the current course's temporary directory. -Defined in FILE.pl +=cut -Creates all of the subdirectories between the directory specified -by C<&getCourseTempDirectory> and the address of the path. +# ^function getCourseTempDirectory +# ^uses $envir{tempDirectory} +sub getCourseTempDirectory { + return $envir{tempDirectory}; +} -Uses +=head2 surePathToTmpFile - &createDirectory($path,$Global::tmp_directory_permission, $Global::numericalGroupID) + $path = surePathToTmpFile($path); -The path may begin with the correct path to the temporary -directory. Any other prefix causes a path relative to the temporary -directory to be created. +Creates all of the intermediate directories between the directory specified by +getCourseTempDirectory() and file specified in $path. -The quality of the error checking could be improved. :-) +If $path begins with the path returned by getCourseTempDirectory(), then the +path is treated as absolute. Otherwise, the path is treated as relative the the +course temp directory. =cut # A very useful macro for making sure that all of the directories to a file have been constructed. +# ^function surePathToTmpFile +# ^uses getCourseTempDirectory +# ^uses createDirectory sub surePathToTmpFile { # constructs intermediate directories if needed beginning at ${Global::htmlDirectory}tmp/ # the input path must be either the full path, or the path relative to this tmp sub directory --- /dev/null +++ macros/parserAssignment.pl @@ -0,0 +1,396 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: pg/macros/parserAssignment.pl,v 1.2.2.1 2008/06/24 00:44:54 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +=head1 NAME + +parserAssignment.pl - Implements assignments to variables + +=head1 DESCRIPTION + +This file implements an assignment operator that allows only a single +variable reference on the left and any value on the right. You can use +this to require students to enter things like + + y = 3x + 1 + +rather than making the "y = " part of the text of the question. This +also allows you to ask for lists of assignments more easily. + +To use it, load the macro file, select the Context you want to use, +add any variables you may need, and enable the assignment operator as +in the following example: + + loadMacros( + "PGstandard.pl", + "MathObjects.pl", + "parserAssignment.pl", + ); + + Context("Numeric")->variables->add(y=>'Real'); + parser::Assignment->Allow; + +Now you can use the equal sign in Formula() objects to create assignments. + + $f = Formula("y = 3x + 1"); + ... + ANS($f->cmp); + +The student will have to make an assignment to the same variable in +order to get credit. For example, he or she could enter y = 1+3x to get +credit for the answer above. + +The left-hand side of an assignment must be a single variable, so + + $f = Formula("3y = 2x"); + +will produce an error. The right-hand side can not include the +variable being assigned on the left, so + + $f = Formula("x = 2x+1"); + +also is not allowed. + +You can produce lists of assignments just as easily: + + $f = Formula("y = 3x, y = 2x-1"); + +and the assignment can be of any type of MathObject. For example: + + Context("Vector")->variables->add(p=>'Vector3D'); + parser::Assignment->Allow; + + $f = Formula("p = <1,2x,1-x>"); + +To produce a constant assignment, use Compute(), as in: + + $p = Compute("p = <1,2,3>"); + +(in fact, Compute() could be used for in place of Formula() in the +examples above as well, since it returns a Formula when the value is +one). + +The left-hand side of an assignment can also be a function +declaration, as in + + f(x) = 3x + 1 + +To allow this, use + + parser::Assignment->Function("f"); + +You can supply more than one function name if you want. E.g., + + parser::Assignment->Function("f","g"); + +The number of variables for these functions is determined by the +assignment itself, so after declaring f to be a function, you can use +either + + f(x) = x+1 +or + f(x,y) = x^2 + y^2 + +provided the variables are defined in the current context. + +Type-checking between the student and correct answers is performed +using the right-hand values of the assignment, and a warning message +will be issued if the types are not compatible. The type of the +variable on the left-hand side, however, is not checked. + +For function declarations, the name of the function and the order +of the variables must match the professor's answer; however, the +names of the variables don't have to match, as long as the function +returns the same results for the same inputs. So + + f(x) = x + 1 +and + f(y) = y + 1 + +will be marked as equal. + +=cut + +# +# FIXME: allow any variables in declaration +# FIXME: Add more hints when variable name isn't right +# or function name or number of arguments isn't right. +# + +sub _parserAssignment_init {parser::Assignment::Init()} + +###################################################################### + +package parser::Assignment; +our @ISA = qw(Parser::BOP); + +sub Init { + main::PG_restricted_eval('sub Assignment {parser::Assignment::List->new(@_)}'); +} + +# +# Check that the left operand is a variable and not used on the right +# +sub _check { + my $self = shift; my $name = $self->{def}{string} || $self->{bop}; + $self->Error("Only one assignment is allowed in an equation") + if $self->{lop}->type eq 'Assignment' || $self->{rop}->type eq 'Assignment'; + $self->Error("The left side of an assignment must be a variable or function",$name) + unless $self->{lop}->class eq 'Variable' || $self->{lop}{isDummy} || $self->context->flag("allowBadOperands"); + if ($self->{lop}{isDummy}) { + my $fvars = $self->{lop}->getVariables; + foreach my $x (keys(%{$self->{rop}->getVariables})) { + $self->Error("The formula for %s can't use the variable '%s'",$self->{lop}->string,$x) + unless $fvars->{$x}; + } + } else { + $self->Error("The right side of an assignment must not include the variable being defined") + if $self->{rop}->getVariables->{$self->{lop}{name}}; + delete $self->{equation}{variables}{$self->{lop}{name}}; + } + $self->{type} = Value::Type('Assignment',2,$self->{rop}->typeRef,list => 1); +} + +# +# Convert to an Assignment object +# +sub eval { + my $self = shift; my $context = $self->context; + my ($a,$b) = ($self->Package("String")->make($context,$self->{lop}->string),$self->{rop}); + $b = Value::makeValue($b->eval,context => $context); + return parser::Assignment::List->make($context,$a,$b); +} + +# +# Don't count the left-hand variable +# +sub getVariables { + my $self = shift; + return $self->{lop}->getVariables if $self->{lop}{isDummy}; + $self->{rop}->getVariables; +} + +# +# Create an Assignment object +# +sub perl { + my $self = shift; + return "parser::Assignment::List->new('".$self->{lop}->string."',".$self->{rop}->perl.")"; +} + +# +# Add/Remove the Assignment operator to/from a context +# +sub Allow { + my $self = shift || "Value"; my $context = shift || $self->context; + my $allow = shift; $allow = 1 unless defined($allow); + if ($allow) { + my $prec = $context->{operators}{','}{precedence}; + $prec = 1 unless defined($prec); + $context->operators->add( + '=' => { + class => 'parser::Assignment', + precedence => $prec+.25, # just above comma + associativity => 'left', # computed left to right + type => 'bin', # binary operator + string => ' = ', # output string for it + } + ); + $context->{value}{Formula} = 'parser::Assignment::Formula'; + $context->{value}{Assignment} = 'parser::Assignment::List'; + } else {$context->operators->remove('=')} + return; +} + +sub Function { + my $self = shift || "Value"; + my $context = (Value::isContext($_[0]) ? shift : $self->context); + Value->Error("You must provide a function name") unless scalar(@_) > 0; + foreach my $f (@_) { + Value->Error("Function name '%s' is illegal",$f) unless $f =~ m/^[a-z][a-z0-9]*$/i; + my $name = $f; $name = $1.'_{'.$2.'}' if ($name =~ m/^(\D+)(\d+)$/); + $context->functions->add( + $f => {class => 'parser::Assignment::Function', TeX => $name, type => $Value::Type{number}} + ); + } +} + +###################################################################### + +# +# A special List object that holds a variable and a value, and +# that prints with an equal sign. +# + +package parser::Assignment::List; +our @ISA = ("Value::List"); + +sub new { + my $self = shift; my $class = ref($self) || $self; + my $context = (Value::isContext($_[0]) ? shift : $self->context); + Value->Error("Too many arguments") if scalar(@_) > 2; + my ($x,$v) = @_; + if (defined($v)) { + my $context = $self->context; + $v = Value::makeValue($v,context=>$context); + if ($v->isFormula) { + $x = $self->Package("Formula")->new($context,$x); + $v->{tree} = parser::Assignment->new($v,"=",$x->{tree},$v->{tree}); + bless $v, $self->Package("Formula"); + return $v; + } + return $self->make($self->Package("String")->make($context,$x),$v); + } else { + $v = $self->Package("Formula")->new($x); + Value->Error("Your formula doesn't seem to be an assignment") + unless $v->{tree}->type eq "Assignment"; + return $v; + } +} + +sub string { + my $self = shift; my ($x,$v) = $self->value; + $x->string . ' = ' . $v->string; +} + +sub TeX { + my $self = shift; my ($x,$v) = $self->value; + $x = $self->Package("Formula")->new($x->{data}[0]); + $x->TeX . ' = ' . $v->string; +} + +# +# Needed since these are called explicitly without an object +# +sub cmp_defaults { + my $self = shift; + $self->SUPER::cmp_defaults(@_); +} + +# +# Class is an a variable assigned to whatever +# +sub cmp_class { + my $self = shift; + my $type = ($self->{data}[0] =~ m/\(/ ? 'Function' : 'Variable'); + "a $type equal to ".$self->{data}[1]->showClass; +} +sub showClass {cmp_class(@_)} + +# +# Return the proper type +# +sub typeRef { + my $self = shift; + Value::Type('Assignment',2,$self->{data}[1]->typeRef,list=>1); +} + +###################################################################### + +# +# A subclass of Formula that does typematching properly for Assignments +# (the match is against the right-hand sides) +# + +package parser::Assignment::Formula; +our @ISA = ("Value::Formula"); + +sub new { + my $self = shift; $class = ref($self) || $self; + my $f = $self->SUPER::new(@_); + bless $f, $class if $f->type eq 'Assignment'; + return $f; +} + +sub typeMatch { + my $self = shift; my $other = shift; my $ans = shift; + return 0 unless $self->type eq $other->type; + $other = $other->Package("Formula")->new($self->context,$other) unless $other->isFormula; + my $typeMatch = ($self->createRandomPoints(1))[1]->[0]{data}[1]; + $main::__other__ = sub {($other->createRandomPoints(1))[1]->[0]{data}[1]}; + $other = main::PG_restricted_eval('&$__other__()'); + delete $main::{__other__}; + return 1 unless defined($other); # can't really tell, so don't report type mismatch + $typeMatch->typeMatch($other,$ans); +} + +sub cmp_class { + my $self = shift; my $value; + if ($self->{tree}{rop}{isConstant}) { + $value = ($self->createRandomPoints(1))[1]->[0]{data}[1]; + } else { + $value = $self->Package("Formula")->new($self->context,$self->{tree}{rop}); + } + my $type = ($self->{tree}{lop}{isDummy} ? "Function" : "Variable"); + return "a $type equal to ".$value->showClass; +} +sub showClass {cmp_class(@_)} + +# +# Convert varaible names to those used in the correct answer, if the +# student answer uses different ones +# +sub compare { + my ($l,$r) = @_; my $self = $l; + my $context = $self->context; + $r = $context->Package("Formula")->new($context,$r) unless Value::isFormula($r); + if ($l->{tree}{lop}{isDummy} && $r->type eq 'Assignment' && $r->{tree}{lop}{isDummy}) { + my ($F,$f) = ($l->{tree}{lop}{params},$r->{tree}{lop}{params}); + if (scalar(@{$F}) == scalar(@{$f})) { + my @subs = (); + for (my $i = 0; $i < scalar(@{$F}); $i++) { + push(@subs,$f->[$i]{name} => $F->[$i]{name}) + unless $F->[$i]{name} eq $f->[$i]{name}; + } + $r = $r->substitute(@subs) if scalar(@subs); + delete $r->{f}; + } + } + $l->SUPER::compare($r,@_); +} + +###################################################################### + +# +# A dummy function that is used for assignments like f(x) = x^2 +# + +package parser::Assignment::Function; +our @ISA = ("Parser::Function"); + +sub _check { + my $self = shift; my %var; + foreach my $x (@{$self->{params}}) { + $self->Error("The arguments of '%s' must be variables",$self->{name}) + unless $x->class eq 'Variable'; + $self->Error("The arguments of '%s' must all be different",$self->{name}) + if $var{$x->{name}}; + $var{$x->{name}} = 1; + } + $self->{type} = $self->{def}{type}; + $self->{isDummy} = 1; +} + +sub eval { + my $self = shift; + $self->Error("Dummy function '%s' can not be evaluated",$self->{name}); +} + +sub call { + my $self = shift; + $self->Error("Dummy function '%s' can not be called",$self->{name}); +} + +1; Index: contextString.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextString.pl,v retrieving revision 1.4.2.1 retrieving revision 1.4.2.2 diff -Lmacros/contextString.pl -Lmacros/contextString.pl -u -r1.4.2.1 -r1.4.2.2 --- macros/contextString.pl +++ macros/contextString.pl @@ -1,28 +1,47 @@ -loadMacros("Parser.pl"); +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader$ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ -sub _contextString_init {}; # don't load it again +=head1 NAME -=head3 Context("String") +contextString.pl - Allow string-valued answers. -########################################################## -# -# Implements contexts for string-valued answers. -# -# You can add new strings to the context as needed -# via the Context()->strings->add() method. E.g., -# -# Context("String")->strings->add(Foo=>{}, Bar=>{alias=>"Foo"}); -# -# Use string_cmp() to produce the answer checker(s) for your -# correct values. Eg. -# -# ANS(string_cmp("Foo")); -# -# + +=head1 DESCRIPTION + +Implements contexts for string-valued answers. + +You can add new strings to the context as needed +via the Context()->strings->add() method. E.g., + + Context("String")->strings->add(Foo=>{}, Bar=>{alias=>"Foo"}); + +Use string_cmp() to produce the answer checker(s) for your +correct values. Eg. + + ANS(string_cmp("Foo")); =cut -package contextString::Variable; +loadMacros("MathObjects.pl"); + +sub _contextString_init {context::String::Init()}; # don't load it again + +################################################## + +package context::String::Variable; sub new { my $self = shift; my $equation = shift; @@ -33,36 +52,59 @@ $equation->Error(["Your answer should be one of %s",$strings]); } -package contextString::Formula; -our @ISA = qw(Value::Formula Parser Value); +################################################## + +package context::String::Formula; +our @ISA = qw(Value::Formula); sub parse { my $self = shift; foreach my $ref (@{$self->{tokens}}) { $self->{ref} = $ref; - contextString::Variable->new($self) if ($ref->[0] eq 'error'); # display the error + context::String::Variable->new($self->{equation}) if ($ref->[0] eq 'error'); # display the error } $self->SUPER::parse(@_); } -package main; +package context::String::BOP::mult; +our @ISA = qw(Parser::BOP); + +sub _check { + my $self = shift; + context::String::Variable->new($self->{equation}); # report an error +} + +################################################## + +package context::String; -$context{String} = Parser::Context->getCopy(undef,"Numeric"); -$context{String}->parens->undefine('|','{','(','['); -$context{String}->variables->clear(); -$context{String}->constants->clear(); -$context{String}->operators->clear(); -$context{String}->functions->clear(); -$context{String}->strings->clear(); -$context{String}->{parser}{Variable} = 'contextString::Variable'; -$context{String}->{parser}{Formula} = 'contextString::Formula'; - -Context("String"); - -sub string_cmp { - my $strings = shift; - $strings = [$strings,@_] if (scalar(@_)); - $strings = [$strings] unless ref($strings) eq 'ARRAY'; - return map {String($_)->cmp(showHints=>0,showLengthHints=>0)} @{$strings}; +sub Init { + my $context = $main::context{String} = Parser::Context->getCopy("Numeric"); + $context->{name} = "String"; + $context->parens->clear(); + $context->variables->clear(); + $context->constants->clear(); + $context->operators->clear(); + $context->functions->clear(); + $context->strings->clear(); + $context->{parser}{Variable} = 'context::String::Variable'; + $context->{parser}{Formula} = 'context::String::Formula'; + $context->operators->add( + ' ' => {precedence => 3, associativity=>"left", type=>"bin", string => "*", class => 'context::String::BOP::mult'}, + '*' => {precedence => 3, associativity=>"left", type=>"bin", class => 'context::String::BOP::mult'} + ); + + main::PG_restricted_eval(<<' END_EVAL'); + sub string_cmp { + my $strings = shift; + $strings = [$strings,@_] if (scalar(@_)); + $strings = [$strings] unless ref($strings) eq 'ARRAY'; + return map {String($_)->cmp(showHints=>0,showLengthHints=>0)} @{$strings}; + } + END_EVAL + + main::Context("String"); ### FIXME: probably should require author to set this explicitly } +1; + Index: PGinfo.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGinfo.pl,v retrieving revision 1.1.2.1 retrieving revision 1.1.2.2 diff -Lmacros/PGinfo.pl -Lmacros/PGinfo.pl -u -r1.1.2.1 -r1.1.2.2 --- macros/PGinfo.pl +++ macros/PGinfo.pl @@ -4,23 +4,32 @@ # All Rights Reserved #################################################################### + =head1 NAME - PGinfo.pl - -Provides macros for determining the values of the current context in which the problem + PGinfo.pl + + +Provides macros for determining the values of the current context in which the problem is being written. -loadMacros("Parser.pl"); +=cut + + +loadMacros("MathObjects.pl"); + + + + =head3 listVariables Usage: listVariables(); -Prints all variables submitted in the problem form and all variables in the +Prints all variables submitted in the problem form and all variables in the the Problem environment and all of the flag variables in Context(). This is used for debugging and to determine the current -context for the problem. +context for the problem. =cut @@ -35,7 +44,7 @@ } =head4 listFormVariables() - + Called by listVariables to print out the input form variables. =cut @@ -48,7 +57,7 @@ } =head4 listEnvironmentVariables() - + Called by listVariables to print out the environment variables (in %envir). =cut @@ -60,8 +69,8 @@ } =head4 listContextFlags() - - Called by listVariables to print out context flags for Math Objects. + + Called by listVariables to print out context flags for Math Objects. =cut @@ -73,21 +82,33 @@ =head3 listContext() Usage: listContext(Context()) - + Prints out the contents of the current context hash -- includes flags and much more =cut -sub listContext { # include +sub listContext { # include my $context = shift; return TEXT("$PAR Error in listContext: usage: listContext(Context()); # must specify a context to list $BR") unless defined $context; foreach $key (keys %$context) { - next if $key =~/^_/; # skip if it begins with _ + next if $key =~/^_/; # skip if it begins with TEXT($HR, $key, $BR); TEXT( pretty_print($context->{$key}) ); } } + +=head3 pp() + + Usage: pp(Hash ); + pp(Object); + + + Prints out the contents of Hash or the instance variables of Object + +=cut + sub pp { my $hash = shift; "printing |". ref($hash)."|$BR". pretty_print($hash); -} \ No newline at end of file +} +1; \ No newline at end of file Index: PGchoicemacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGchoicemacros.pl,v retrieving revision 1.8 retrieving revision 1.8.2.1 diff -Lmacros/PGchoicemacros.pl -Lmacros/PGchoicemacros.pl -u -r1.8 -r1.8.2.1 --- macros/PGchoicemacros.pl +++ macros/PGchoicemacros.pl @@ -1,380 +1,331 @@ - -BEGIN{ - be_strict; -} - -package main; - +################################################################################ +# WeBWorK Program Generation Language +# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader$ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ =head1 NAME -PGchoicemacros.pl --- located in the courseScripts directory +PGchoicemacros.pl - Macros for multiple choice, matching, and true/false questions. +=head1 SYNOPSIS -=head1 SYNPOSIS - -=pod - -There are two types of choice macros. The older versions are simply scripts. -The newer versions involve the "List.pm" class and its sub-classes -and the use of objects based on these classes. The list sub-classes are: -"Match.pm" which aids in setting up matching question - and answer lists, "Select.pm" which aids in selecting - and presenting a subset of questions with short answers -(e.g. true/false questions) from a larger question set, and -"Multiple.pm" which aids in setting up a -standard style, one question, many answers type multiple -choice question. +Matching example: + loadMacros("PGchoicemacros.pl"); + + # create a new match list + $ml = new_match_list(); + + # enter three questions and their answers + $ml->qa( + "What color is a rose?", + "Red", + "What color is the sky?", + "Blue", + "What color is the sea?", + "Green", + ); + + # choose two of these questions, ordered at random, + # which will be printed in the problem. + $ml->choose(2); + + # print the question and answer choices + BEGIN_TEXT + Match the answers below with these questions: $BR + \{ $ml->print_q \} $BR + Answers: + \{ $ml->print_a \} + END_TEXT + + # register the correct answer + ANS($ml->ra_correct_ans); =head1 DESCRIPTION -Sample usage: - - - $ml = new_match_list(); - # enter three questions and their answers - $ml->qa( "What color is a rose?", - "Red", - "What color is the sky?", - "Blue", - "What color is the sea?", - "Green" - ); - # choose two of these questions, ordered at random, - # which will be printed in the problem. - $ml->choose(2); - BEGIN_TEXT - Match the answers below with these questions:$BR - \\{$ml->print_q\\} $BR - Answers: - \\{$ml->print_a\\} - END_TEXT - - ANS( $ml->ra_correct_ans() ); - -=cut - - -=head2 Matching List macros - - -=head3 new_match_list - -Matching list object creation macro - -Usage: - - - $ml = new_match_list(); - -Which is short hand for the following direct call to Match - - $ml = new Match(random(1,2000,1), ~~&std_print_q, ~~&std_print_a); - - -Either call will create a matching list object in the variable $ml. -(I< Note: $ml cannot be a my variable if it is to be used within a BEGIN_TEXT/END_TEXT block.>) - -The first argument is the seed for the match list (choosen at random between 1 and 2000 in -the example above.). The next two arguments are references to the print subroutines -used to print the questions and the answers. -Other printing methods can be used instead of the standard ones. An -example of how to do this is demonstrated with -"pop_up_list_print_q" below. - -=head4 std_print_q - -Standard method for formatting a list of questions with answer blanks. - -This formatting routine is the default method for formatting the -way questions are printed -for each of the three sub-classes of "List.pm". It lists the questions vertically, numbering -them sequentially and providing an answer blank before each question. -C<std_print_q> checks which mode the user is trying to print the -questions from and returns the appropriately formatted string. - -The length of the answer blank can be set with C<$ml-> +There are two types of choice macros. The older versions are simple subroutines. +The newer versions involve the List class and its sub-classes and the use of +objects based on these classes. The list sub-classes are: -To replace the standard question formatting method with your own, use: +=over - $ml->rf_print_q(~~&my_question_format_method) +=item * -Your method should be a subroutine of the form C<my_question_format_method($self, @questions)> -and should return a string to be printed. The @questions array contains the -questions to be listed, while $self can be used to obtain extra information from -the object for formatting purposes. The variable C<$main::displayMode> contains the -current display mode. (See "MODES" for more details on display modes and -see "writing print methods for lists" for details on constructing formatting subroutines.) +B<Match>, which aids in setting up matching question and answer lists, +=item * -=head4 std_print_a +B<Select>, which aids in selecting and presenting a subset of questions with short +answers (e.g. true/false questions) from a larger question set, and -Standard method for formatting a list of answers. +=item * -This simple formatting routine is the default method for formatting -the answers for matching lists. It lists the answers vertically -lettered sequentially. - -To replace the standard answer formatting method with your own subroutine use: - - $ml->rf_print_q(~~&my_answer_format_method) - -The answer formatting method has the same interface as the question formatting -method. - - -=head2 Select List macros - - -=head3 new_select_list - -Select list object creation macro - -Usage: - - $sl = new_select_list; - -Which is equivalent to this direct call to Select - - $sl = new Select(random(1,2000,1), ~~&std_print_q, ~~&std_print_a); - - -Either call will create a select list object in the variable $sl. ( Note that -$sl cannot be a my variable if it is to be used within a BEGIN_TEXT/END_TEXT -block.) The printing methods are the same as those defined for C<new_match_list> -above. -See the documentation for "Select.pm" to see how to use this -object to create a true/false question. - -std_print_a is only intended to be used for debugging with select lists, as there is rarely a reason to -print out the answers to a select list. - - -=head3 new_pop_up_select_list - - - -Usage: - - $sl = new_pop_up_select_list;</I></PRE> - -Which is equivalent to this direct call to Select - - $sl = new Select(random(1,2000,1), ~~&pop_up_list_print_q, ~~&std_print_a); - - -Either call will create a select list object in the variable $sl. ( Note that -$sl cannot be a my variable if it is to be used within a BEGIN_TEXT/END_TEXT -block.) The printing methods are passed as references (~~ in PG equals \ in -perl) to subroutines so that no matter what printing subroutines are used, -those subroutines can be used by saying $sl->print_q and $sl->print_a. This -also means that other subroutines can be used instead of the default ones. +B<Multiple>, which aids in setting up a standard one-question-many-answers multiple +choice question. -See the documentation for <a href='Select'>Select.pm</a> to see how to use this - object to create a true/false question. +=back +=cut -=head4 std_print_q +# ^uses be_strict +BEGIN{ + be_strict; +} -Standard method for printing questions with answer boxes +package main; -See std_print_q under Matching Lists above. +BEGIN { + be_strict(); +} -=head4 pop_up_list_print_q +# ^function _PGchoicemacros_init -Alternate method for print questions with pop up lists. +sub _PGchoicemacros_init{ +} -Usage: +=head1 MACROS -This printing routine is used to print the questions for a true/false or other -select list with a preceding pop up list of possible answers. A list of values -and labels need to be given to the pop_up_list so that the intended answer is -returned when a student selects an answer form the list. Notethe use of => to -associate the values on the left with the labels on the right, this means that, -for instance, the student will see the word True in the pop_up_list but the -answer that is returned to the grader is T, so that it corresponds with what -the professor typed in as the answer when using $sl->qa('blah blah', 'T'); +=cut -=for html - <PRE> - <I>$sl->ra_pop_up_list([</I>value<I> => </I>label<I>, - T => 'True', - F => 'False']);</I></PRE> +################################################################################ +=head2 Match lists -=head4 std_print_a +=over -This is only intended to be used for debugging as there is rarely a reason to -print out the answers to a select list. +=item new_match_list -See std_print_a under Matching Lists above. + $ml = new_match_list(); +new_match_list() creates a new Match object and initializes it with sensible +defaults. It is equivalent to: -=head2 Multiple Choice macros + $ml = new Match(random(1,2000,1), ~~&std_print_q, ~~&std_print_a); +The first argument is the seed for the match list (choosen at random between 1 +and 2000 in the example above). The next two arguments are references to the +print subroutines used to print the questions and the answers. Other printing +methods can be used instead of the standard ones. An example of how to do this +is demonstrated with pop_up_list_print_q() below. -=head3 new_multiple_choice +=cut -Multiple choice object creation macro +# ^function new_match_list +# ^uses Match::new +# ^uses &std_print_q +# ^uses &std_print_a -Usage: +sub new_match_list { + new Match(random(1,2000,1), \&std_print_q, \&std_print_a); +} -=for html - <PRE> - <I>$mc = new_multiple_choice;</I></PRE> +=back -Which is equivalent to this direct call to Multiple +=cut -=for html - <PRE> - <I>$mc = new Multiple(random(1,2000,1), ~~&std_print_q, ~~&std_print_a);</I></PRE> +################################################################################ -Either call will create a multiple choice object in the variable $mc. Note that -$mc cannot be a my variable if it is to be used within a BEGIN_TEXT/END_TEXT -block. +=head2 Select lists -=for html - <P>See the documentation for <a href='Multiple'>Multiple.pm</a> to see how to use - this object to create a multiple choice question. +=over +=item new_select_list -=head4 std_print_q + $sl = new_select_list(); -Standard method for printing questions +new_select_list() creates a new Select object and initializes it with sensible +defaults. It is equivalent to: -See std_print_q under Matching Lists above. + $sl = new Select(random(1,2000,1), ~~&std_print_q, ~~&std_print_a); +The parameters to the Select constructor are the same as those for the Match +constrcutor described above under new_match_list(). -=head4 radio_print_a +See the documentation for the Select class to see how to use this object to +create a true/false question. -Method for printing answers with radio buttons +std_print_a is only intended to be used for debugging with select lists, as +there is rarely a reason to print out the answers to a select list. -This simple printing routine is used to print the answers to multiple choice -questions in a bulleted style with radio buttons preceding each possible answer. -When a multiple choice object is created, a reference to radio_print_a is passed -to that object so that it can be used from within the object later. +=cut -radio_print_a checks which mode the user is trying to print the answers from and -returns the appropriately formatted string. +# ^function new_select_list +# ^uses Select::new +# ^uses &std_print_q +# ^uses &std_print_a +sub new_select_list { + new Select(random(1,2000,1), \&std_print_q, \&std_print_a); +} -=head3 new_checkbox_multiple_choice +=item new_pop_up_select_list() -Checkbox multiple choice object creation macro + $sl = new_pop_up_select_list(); -Usage: +new_popup_select_list() creates a new Select object and initializes it such that +it will render as a popup list. It is equivalent to: -=for html - <PRE> - <I>$cmc = new_checkbox_multiple_choice;</I></PRE> + $selectlist = new Select(random(1,2000,1), ~~&pop_up_list_print_q, ~~&std_print_a); -Which is equivalent to this direct call to Multiple +=cut -=for html - <PRE> - <I>$cmc = new Multiple(random(1,2000,1), ~~&std_print_q, ~~&checkbox_print_a);</I></PRE> +# ^function new_pop_up_select_list +# ^uses Select::new +# ^uses &pop_up_list_print_q +# ^uses &std_print_a -Either call will create a checkbox multiple choice object in the variable $cmc. Note that -$cmc cannot be a my variable if it is to be used within a BEGIN_TEXT/END_TEXT -block. +sub new_pop_up_select_list { + new Select(random(1,2000,1), \&pop_up_list_print_q, \&std_print_a); +} -=for html - <P>See the documentation for <a href='Multiple'>Multiple.pm</a> to see how to use - this object to create a multiple choice question. +=back +=cut -=head4 std_print_q +################################################################################ -Standard method for printing questions +=head2 Multiple choice quesitons -See std_print_q under Matching Lists above. +=over +=item new_multiple_choice() -=head4 checkbox_print_a + $mc = new_multiple_choice(); -Method for printing answers with radio buttons +new_multiple_choice() creates a new Multiple object that presents a question and +a number possible answers, only one of which can be chosen. It is equivalent to: -This simple printing routine is used to print the answers to multiple choice -questions in a bulleted style with checkboxes preceding each possible answer. -When a multiple choice object is created, a reference to checkbox_print_a is passed -to that object so that it can be used from within the object later. + $mc = new Multiple(random(1,2000,1), ~~&std_print_q, ~~&radio_print_a); -checkbox_print_a checks which mode the user is trying to print the answers from and -returns the appropriately formatted string. +The parameters to the Multiple constructor are the same as those for the Match +constrcutor described above under new_match_list(). +=cut +# ^function new_multiple_choice +# ^uses Multiple::new +# ^uses &std_print_q +# ^uses &radio_print_a -=cut -BEGIN { - be_strict(); -} -sub _PGchoicemacros_init{ +sub new_multiple_choice { + new Multiple(random(1,2000,1), \&std_print_q, \&radio_print_a); } -=head4 new_match_list +=item new_checkbox_multiple_choice() - Usage: $ml = new_match_list(); + $mc = new_checkbox_multiple_choice(); +new_checkbox_multiple_choice() creates a new Multiple object that presents a +question and a number possible answers, any number of which can be chosen. It is +equivalent to: -Note that $ml cannot be a my variable if used within a BEGIN_TEXT/END_TEXT block + $mc = new Multiple(random(1,2000,1), ~~&std_print_q, ~~&checkbox_print_a); =cut -sub new_match_list { - new Match(random(1,2000,1), \&std_print_q, \&std_print_a); +# ^function new_checkbox_multiple_choice +# ^uses Multiple::new +# ^uses &std_print_q +# ^uses &checkbox_print_a +sub new_checkbox_multiple_choice { + new Multiple(random(1,2000,1), \&std_print_q, \&checkbox_print_a); } -=head4 new_select_list - sage: $sl = new_select_list(); - -Note that $sl cannot be a my variable if used within a BEGIN_TEXT/END_TEXT block +=back =cut -sub new_select_list { - new Select(random(1,2000,1), \&std_print_q, \&std_print_a); -} - -=head4 new_pop_up_select_list; +################################################################################ - Usage: $pusl = new_pop_up_select_list(); +=head2 Question printing subroutines -=cut +=over -sub new_pop_up_select_list { - new Select(random(1,2000,1), \&pop_up_list_print_q, \&std_print_a); -} +=item std_print_q() -=head4 new_multiple_choice + # $list can be a matching list, a select list, or a multiple choice list + $list->rf_print_q(~~&std_print_q); + TEXT($list->print_q); - Usage: $mc = new_multiple_choice(); +This formatting routine is the default method for formatting the way questions +are printed for each of the three List sub-classes. It lists the questions +vertically, numbering them sequentially and providing an answer blank before +each question. std_print_q() checks which mode the user is trying to print the +questions from and returns the appropriately formatted string. =cut -sub new_multiple_choice { - new Multiple(random(1,2000,1), \&std_print_q, \&radio_print_a); -} +# ^function std_print_q -=head4 new_checkbox_multiple_choice +sub std_print_q { + my $self = shift; + my (@questions) = @_; + my $length = $self->{ans_rule_len}; + my $out = ""; + #if ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') { + if ($main::displayMode =~ /^HTML/) { + my $i=1; my $quest; $out = "\n<P>\n"; + foreach $quest (@questions) { + $out.= ans_rule($length) . " <B>$i.</B> $quest<BR>"; + $i++; + } + } elsif ($main::displayMode eq 'Latex2HTML') { + my $i=1; my $quest; $out = "\\par\n"; + foreach $quest (@questions) { + $out.= ans_rule($length) . "\\begin{rawhtml}<B>$i. </B>\\end{rawhtml} $quest\\begin{rawhtml}<BR>\\end{rawhtml}\n"; + $i++; + } + } elsif ($main::displayMode eq 'TeX') { + $out = "\n\\par\\begin{enumerate}\n"; + my $i=1; my $quest; + foreach $quest (@questions) { + $out .= "\\item[" . ans_rule($length) . "$i.] $quest\n"; + $i++; + } + $out .= "\\end{enumerate}\n"; + } else { + $out = "Error: PGchoicemacros: std_print_q: Unknown displayMode: $main::displayMode.\n"; + } + $out; - Usage: $mcc = new_checkbox_multiple_choice(); +} -=cut +=item pop_up_list_print_q() -sub new_checkbox_multiple_choice { - new Multiple(random(1,2000,1), \&std_print_q, \&checkbox_print_a); -} + $sl->rf_print_q(~~&pop_up_list_print_q); + $sl->ra_pop_up_list([T => 'True', F => 'False']); + TEXT($sl->print_q); -=head4 initializing a pop_up_list +Alternate method for print questions with pop up lists. - Usage: $sl->rf_print_a(~~&pop_up_list_print_q); - $sl->ra_pop_up_list([</I>value<I> => </I>label<I>, T => 'True', F => 'False']); +This printing routine is used to print the questions for a true/false or other +select list with a preceding pop up list of possible answers. A list of values +and labels need to be given to the pop_up_list so that the intended answer is +returned when a student selects an answer form the list. Note the use of => in +the example above to associate the values on the left with the labels on the +right, this means that, for instance, the student will see the word True in the +pop_up_list but the answer that is returned to the grader is T, so that it +corresponds with what the professor typed in as the answer when using +$sl->qa('blah blah', 'T'); =cut + +# ^function pop_up_list_print_q + sub pop_up_list_print_q { my $self = shift; my (@questions) = @_; @@ -413,22 +364,23 @@ } -# For graphs in a matching question. -#sub format_graphs { -# my $self = shift; -# my @in = @_; -# my $out = ""; -# while (@in) { -# $out .= shift(@in). "#" ; -# } -# $out; -#} +=item quest_first_pop_up_list_print_q() + $sl->rf_print_q(~~&quest_first_pop_up_list_print_q); + $sl->ra_pop_up_list([T => 'True', F => 'False']); + TEXT($sl->print_q); + +Similar to pop_up_list_print_q(), but places the popup list after the question +text in the output. + +=cut # To put pop-up-list at the end of a question. # contributed by Mark Schmitt 3-6-03 +# ^function quest_first_pop_up_list_print_q + sub quest_first_pop_up_list_print_q { my $self = shift; my (@questions) = @_; @@ -467,9 +419,24 @@ $out; } + +=item ans_in_middle_pop_up_list_print_q() + + $sl->rf_print_q(~~&ans_in_middle_pop_up_list_print_q); + $sl->ra_pop_up_list([T => 'True', F => 'False']); + TEXT($sl->print_q); + +Similar to quest_first_pop_up_list_print_q(), except that no linebreaks are +printed between questions, allowing for the popup list to be placed in the +middle of the text of a problem. + +=cut + # To put pop-up-list in the middle of a question. # contributed by Mark Schmitt 3-6-03 +# ^function ans_in_middle_pop_up_list_print_q + sub ans_in_middle_pop_up_list_print_q { my $self = shift; my (@questions) = @_; @@ -509,10 +476,18 @@ } +=item units_list_print_q + +A simple popup question printer. No question text is printed, instead the +pop_up_list contents only are printed as a popup menu. + +=cut # Units for physics class # contributed by Mark Schmitt 3-6-03 +# ^function units_list_print_q + sub units_list_print_q { my $self = shift; my (@questions) = @_; @@ -525,7 +500,29 @@ $out; } +=back + +=cut + +################################################################################ + +=head2 Answer printing subroutines + +=over + +=item std_print_a + + # $list can be a matching list, a select list, or a multiple choice list + $list->rf_print_a(~~&std_print_a); + TEXT($list->print_a); + +This simple formatting routine is the default method for formatting the answers +for matching lists. It lists the answers vertically lettered sequentially. + +=cut + #Standard method of printing answers in a matching list +# ^function std_print_a sub std_print_a { my $self = shift; my(@array) = @_; @@ -560,10 +557,26 @@ } +=item radio_print_a() + + # $list can be a matching list, a select list, or a multiple choice list + $list->rf_print_q(~~&radio_print_q); + TEXT($list->print_q); + +This simple printing routine is used to print the answers to multiple choice +questions in a bulleted style with radio buttons preceding each possible answer. +When a multiple choice object is created, a reference to radio_print_a is passed +to that object so that it can be used from within the object later. +radio_print_a checks which mode the user is trying to print the answers from and +returns the appropriately formatted string. +=cut #Alternate method of printing answers as a list of radio buttons for multiple choice +#Method for naming radio buttons is no longer round about and hackish + +# ^function radio_print_a sub radio_print_a { my $self = shift; my (@answers) = @_; @@ -603,8 +616,25 @@ } -#Second alternate method of printing answers as a list of radio buttons for multiple choice -#Method for naming radio buttons is no longer round about and hackish +=item checkbox_print_a() + + # $list can be a matching list, a select list, or a multiple choice list + $list->rf_print_q(~~&radio_print_q); + TEXT($list->print_q); + +This simple printing routine is used to print the answers to multiple choice +questions in a bulleted style with checkboxe... [truncated message content] |