CompoundProblems

From WeBWorK_wiki
Jump to navigation Jump to search

Compound, MultiPart Problems Worked Sequentially

This article has been retained as a historical document. It is not up-to-date and the formatting may be lacking. Use the information herein with caution.
Outdated
IMPORTANT: The content of this page is outdated. Do not rely on the information contained in this page.
You can write multi-part problems using the scaffold.pl macro file.


This is the PG code to create a problem with multiple parts that are displayed sequentially. We show two ways of doing this, one by explicitly referring to the scores in the answer hashes for previous parts to the problem and the second by using the compoundProblem.pl macro file.

Problem Techniques Index

Using answer hashes

This way of creating a compound problem has the advantage of being slightly cleaner from the perspective of students working the problem.

PG problem file Explanation
DOCUMENT();
loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"parserPopUp.pl",
);
TEXT(beginproblem());

Initialization: We need make no changes to the initialization section of the file. In this example we include parserPopUp.pl so that we can use that.

$a = random(1,9,2);
$evenOdd = PopUp( [ "?", "even", "odd" ], 
                  "odd" );
$evenNum = Compute( 2 );

Setup: In the problem set-up section of the file we define some variables that we will use in the problem, as usual.

Context()->texStrings;
BEGIN_TEXT
${BBOLD}Part 1 of 2:$EBOLD
$PAR
Is \( $a \) even or odd?
\{ $evenOdd->menu() \}
END_TEXT
Context()->normalStrings;

ANS( $evenOdd->cmp() );

$ans_hash1 = $evenOdd->cmp()->evaluate( 
    $inputs_ref->{ANS_NUM_TO_NAME(1)} );

if ( $ans_hash1->{score} == 1 ) {
Context()->texStrings;
BEGIN_TEXT
${BBOLD}Part 2 of 2:$EBOLD
$PAR
Enter an example of an even number,
\(n\): \(n = \) \{ $evenNum->ans_rule() \}
END_TEXT
Context()->normalStrings;

ANS( $evenNum->cmp( checker=>sub {
    my ( $cor, $stu, $ans ) = @_;
    return 0 == ($stu % 2); } ) );

} # ends if ( $ans_hash1->{score} ) 

Main Text: The text section of the problem is now broken into the parts that we want the student to work sequentially. Here we always display the first part of the problem, and then display the second part only after the student has correctly worked the first part.

Note that if we have multiple answer blanks in the first part of the problem the numbers used to dereference the answer numbers must increment: e.g., if we had two true/false pop-ups, $evenNum1 and $evenNum2, we would test for completeness of the first part of the problem with

    $ans_hash1 = $evenNum1->cmp()->evaluate( 
        $inputs_ref->{ANS_NUM_TO_NAME(1)} );
    $ans_hash2 = $evenNum2->cmp()->evaluate(
        $inputs_ref->{ANS_NUM_TO_NAME(2)} );
    if ( $ans_hash1->{score} == 1 && 
         $ans_hash2->{score} == 1 ) {
        # part 2 of the problem

(The line breaks putting the argument of the evaluate() method are included only to keep the line lengths short and improve formatting in the multi-column format of this page.)


ENDDOCUMENT();

Answer evaluation: We've included the answer evaluators in the conditionals bracketing the display of the text, so they do not appear here. Solutions would, of course.

Using compoundProblem.pl

This way of creating a compound problem has the advantage of being slightly easer to manage.

PG problem file Explanation
DOCUMENT();
loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"parserPopUp.pl",
"compoundProblem.pl",
);
TEXT(beginproblem());

Initialization: In the initialization section of the file, load compoundProblem.pl. In this example we also include parserPopUp.pl so that we can use that.

$a = random(1,9,2);
$evenOdd = PopUp( [ "?", "even", "odd" ], 
                  "odd" );
$evenNum = Compute( 2 );

$cp = new compoundProblem( parts=>2, 
                           weights=>[1,2],
                           nextStyle=>'Forced' );

Setup: In the problem set-up section of the file we define some variables that we will use in the problem, and then define the compoundProblem object $cp with the new compoundProblem() call. The arguments of compoundProblem are documented in the source file, and include parts=>n (the number of parts in the problem), weights=>[n1,...,nm] (the relative weight for each part of the problem). Other arguments are indicated below.

The key method of the $cp that is used to control what is displayed in the problem is the part method, which reports which part of the problem the students is working on; we use this to control the text displayed, as shown in the following section of the file.

Context()->texStrings;
BEGIN_TEXT
Is \( $a \) even or odd?
\{ $evenOdd->menu() \}
END_TEXT
Context()->normalStrings;

ANS( $evenOdd->cmp() );

if ( $cp->part > 1 ) {
Context()->texStrings;
BEGIN_TEXT
Enter an example of an even number,
\(n\): \(n = \) \{ $evenNum->ans_rule() \}
END_TEXT
Context()->normalStrings;

ANS( $evenNum->cmp( checker=>sub {
    my ( $cor, $stu, $ans ) = @_;
    return 0 == ($stu % 2); } ) );

} # ends if ( $cp->part > 1 ) 

Main Text: The text section of the problem is now broken into the parts that we want the student to work sequentially. Here we always display the first part of the problem, and then display the second part only after the student has correctly worked the first part and clicked "submit" to continue to the second part. The answer checkers are similarly associated with the text sections that are being displayed.


ENDDOCUMENT();

Answer evaluation: We've included the answer evaluators in the conditionals bracketing the display of the text, so they do not appear here. Solutions would, of course.

As noted above, there are a number of other options that can be supplied to the compoundProblem object. These include:

  • parts => n: The number of parts in the problem. If not provided, defaults to 1, which rather defeats the point of using this in the first place.
  • weights => [n1,...,nm]: The relative weights to give to each part in the problem. For example, weights => [2,1,1] would cause the first part to be worth 50% of the points (twice the amount for each of the other two), while the second and third part would be worth 25% each. If weights are not supplied, the parts are weighted by the number of answer blanks in each part (and you must provide the total number of blanks in all the parts by supplying the totalAnswers option).
  • totalAnswers => n: The total number of answer blanks in all the parts put together (this is used when computing the per-part scores, if part weights are not provided).
  • saveAllAnswers => 0 or 1: Usually, the contents of named answer blanks from previous parts are made available to later parts using variables with the same name as the answer blank. Setting saveAllAnswers to 1 will cause ALL answer blanks to be available (via variables like $AnSwEr1, and so on). If not provided, defaults to 0.
  • parserValues => 0 or 1: Determines whether the answers from previous parts are returned as MathObjects (like those returned from Real(), Vector(), etc) or as strings (the unparsed contents of the student answer). If you intend to use the previous answers as numbers, for example, you would want to set this to 1 so that you would get the final result of any formula the student typed, rather than the formula itself as a character string. If not provided, defaults to 0.
  • nextVisible => type: Tells when the "go on to the next part" option is available to the student. The possible types include: ifCorrect (the default; next is available only when all the answers are correct), Always (next is always available (but note that students can't go back once they go on), and Never (the problem controls going on by itself).
  • nextStyle => type: Determines the style of "next" indicator to display (when it is available). The possible types include: CheckBox (the default), Button, Forced (forces students to go on when they next submit answers), and HTML (provide an arbitrary HTML string).

(These are all taken directly from the documentation in the macro file.)

Problem Techniques Index