Keeping Random Variables Between Answer Submissions

It is important to use random numbers for variables when one wants different students to get different questions (concomitantly this means that the same student, coming back to a problem at a latter date will most likely get a new version of an old question, i.e., with new numbers). On the other hand, given the nature of the WWW, we want the numbers preserved between invocations obtained by pressing the Submit button. Therefore, we have to be careful, repeat careful, to make sure that random variables are passed properly (i.e., retained). The following program illustrates the proposed process, once as direct calls to CGI.pm, once as a subroutine.
#!/usr/local/bin/perl -- -*-perl-*-
use CGI;
$query = new CGI;

$title = "hidden";#change this for real applications

print $query->header;
print $query->start_html($title);
srand;
if ( $title eq "Prototype CATutils"){print "<font color=red ><h1>Change the title</h1></font>";}
else{
  print "<h1>$title</h1>";
}
These first few lines of code are common to most applications. We first make CGI.pm available, then we print a header, and get the title up. Since we have cloned this from a prototype, we include the ``if'' statement, to make us super aware that we have or have not altered the title from the title included in the prototype.
Next, we generate three random numbers, which are (here) rates, to be used presumably in a rate times time problem.
$rate1 = int(90*$q1->rno(2)+4);
$rate2 = int(90*$q1->rno(2)+4);
$rate3 = int(90*$q1->rno(2)+4);
print $query->start_form;
Having generated the random numbers, we begin a question.
print "<font color=red>";
print "(IN MAIN)the random number generator generated rate1 = $rate1.";
print "Since this one is being saved, this new value will be lost, and the old
one will be restored upon submission.";
print "</font>";
print "<font color=yellow>";
print "<br> (IN MAIN)the random number generator generated rate2 = $rate2 .";
print "Since this one is being saved, this new value will be lost, and the old
one will be restored upon submission.";
print "</font>";
print "<font color=green>";
print "<br> (IN MAIN)the random number generator generated rate3 = $rate3";
print "</font>";
The above code has been adjusted to color code the kinds of saves which are being used. Red implies a traditional save, yellow denotes a subroutine based save, and green implies no save what so ever. What we mean is that the last rate, rate3, will change as the student attempts to answer the question, i.e., enters a number and pressing the Submit button. The other two variables, rate1 and rate2, will be preserved each time the student presses the return key. This does not mean that the student will get the same random variables if s/he returns some other time to this question. It is important to realize that the scheme presented here preserves the random number values only during the current session.
In order to preserve the values of rate1 and rate2 between different sessions, so that a student returning tomorrow would obtain the same random numbers as s/he did today, you would have to save the random variables to disk, and restore them when this particular student returns, implying some kind of login scheme for users. You can see that this becomes inceasingly elaborate. We will live with the current scheme for the time being.
#------------------------------------------------------beginning of traditional saves
if($query->param('rate1') eq ""){ $query->param('rate1',$rate1);}
The first line of the traditional save executes fully only if the variable, in this case the CGI.pm variable 'rate1' is NULL. Then, and only then, the CGI.pm variable 'rate1' is initialized with the value $rate1. Since this is the value generated by the random number generator, this is the value the student will be stuck with for the duration of his/her session. The assignment of a CGI.pm variable's value is achieved solely using the statement:
$query->param('rate1',$rate1);
and you have to keep clear in your mind the difference between a Perl variable and a CGI.pm variable. Assignments of Perl variables can be achieved using the equal sign, but the CGI.pm variable, which may lexicographically be similar, is distinct, and needs to be handled differently.
print $query->hidden('rate1',$rate1);
This statement (above) is the second part of save sequence, hiding this particular variable.
#------------------------------------------------------end of traditional saves
$rate1 = $query->param('rate1');
#------------------------------------------------------end of traditional restores
This last statement restores the variable $rate1 to its CGI.pm value. The second time this script is called, upon submission of an answer, the $rate1 value will be reassigned to a new random number, and this assignment statement restores the save CGI.pm value, which was the first value ever generated.
Now we turn to the subroutine method of saving variables. Although we could write this out in one line, for the purposes of education, it is better to write each line separately, so that one can see what is going on.
#------------------------------------------------------beginning of subroutine based saves
$r2 = \$rate2;
This first line of code is part of the ``pointer'' approach which Perl shares with several other computer languages. $r2 is assigned not the value of $rate2, but the address where this is stored. This address is called a pointer.
%list = ('rate2',$r2);
This second line of code assigns a list, the first element of which is a string, 'rate2', and the second is the pointer to where this Perl variable of similar name is stored.
&save_variables(%list);
Here we call a subroutine, whose argument list is the list mentioned above. Note that we could have used
&save_variables('rate2',$r2);
just as well. The original subroutine had a ``forall'' loop to do all ordered pairs in the list, so that we could save all variables at once, but the newer version used here must be called each time a variable needs saving.
#------------------------------------------------------end of traditional restores

print "<br> enter a number";
print $query->textfield('number','',30,50);
The question which is to be posed to the student is now inserted here. Our example is just plain silly, but who cares?
print "<br><font color=blue>Here is a dump, showing the variables CGI.pm is using:</font>";
print $query->dump;
Here we dump the CGI.pm variables so you can see that there are three of them, rate1, rate2, and number. rate3 is a Perl variable, but not a CGI.pm variable.
print <<EOF;
<br>
(IN MAIN)
<br>
<font color=red>
rate1 is being changed at every invocation and being saved in the normal
manner. Therefore, it is having the same value at each invocation: $rate1
</font>
<font color=yellow>
<br>Here is rate2, using the save SUBROUTINE which
should have the same value at each invocation: $rate2
</font>
<font color=green>
<br>Meanwhile, the third random number (rate3), not being hidden and saved, was $rate3
</font>
EOF
The above code is meant to illustrate how the numbers are being saved, so that if you exercise this code, you will see, in one color, the rate1 information, in another the rate2 information, and finally in a third color, the rate3 information.
#=========start
print "<br>",$query->submit;
print $query->end_form;
$number = $query->param('number');
$ans = $number*$rate2;
print "<br>answer = number * <font color=yellow>rate2</font> = $number* <font color=yellow>$rate2</font> = $ans";
As usual, once the form has ended, we get the student's response(s) and do something with those responses.
print $query->end_html;
#---------------------------------------------
sub save_variables{
  my $var_string = $_[0];
  my $pointer_to_var_string = $_[1];
The variables, which are local to the subroutine, are brought into the subroutine via these two lines. This is standard Perl.
  my $debug = "false";
  $var_string = "'".$var_string."'";#adjust for future use in CGI.pm
Here, we adjust rate1, the string which is in $var-string, to look like 'rate1', i.e., bracket the string with apostrophes. This is because we need that form to pass to CGI.pm
  if($debug eq "true"){
    print "<br> (IN SUBROUTINE)var-string = ",$var_string;
    print "<br> (IN SUBROUTINE) qw{$query->param($var_string)}";
    print "<br> (IN SUBROUTINE)", $query->param($var_string);
    print " <br>   and \ (second variable)= ",$pointer_to_var_string;
  }
  $a = $$pointer_to_var_string;#variable passed by pointer $_[1]
Here, we recoved the value of $rate1 which was not passed to the subroutine. Rather, the pointer to the $rate1 variable was passed, so we have to look there for the value, i.e., where the pointer is pointing.
  if($debug eq "true"){
    print "<br>(IN SUBROUTINE)called value \(loc= ",$pointer_to_var_string,") = ",$a;
  }
  if($query->param($var_string) eq '')
		{ $query->param($var_string,$$pointer_to_var_string);#if first time, set
Here we have the first part of the save sequence. If the CGI.pm variable is unassigned, assign it.
    if($debug eq "true"){
      print "<br>(IN SUBROUTINE) param was blank";
    }
  }#save it
  print $query->hidden($var_string,$$pointer_to_var);
Here we have the second part of the save sequence, where we hide the variable.
  if($debug eq "true"){
    print "<br>(IN SUBROUTINE) restored value(from query->param) ",
    $query->param($var_string);
  }
  $$pointer_to_var_string = $query->param($var_string);#update value pointed at by $_[1]
Finally, here we restore the Perl variable to the value of the CGI.pm variable.
  print "<br><br>=====";
}
Do we need (or want) this last line?

To try this script Use this Pointer

#!/usr/local/bin/perl -- -*-perl-*-
use CGI;
$query = new CGI;
use CATutils;
$q1 = new CATutils;

$title = "hidden";#change this for real applications

print $query->header;
print $query->start_html($title);
require  "count.pl";
srand;
if ( $title eq "Prototype CATutils"){print "<font color=red ><h1>Change the title</h1></font>";}
else{
  print "<h1>$title</h1>";
}

$rate1 = int(90*$q1->rno(2)+4);
$rate2 = int(90*$q1->rno(2)+4);
$rate3 = int(90*$q1->rno(2)+4);
print $query->start_form;
print "<font color=red>";
print "(IN MAIN)the random number generator generated rate1 = $rate1.";
print "Since this one is being saved, this new value will be lost, and the old
one will be restored upon submission.";
print "</font>";
print "<font color=yellow>";
print "<br> (IN MAIN)the random number generator generated rate2 = $rate2 .";
print "Since this one is being saved, this new value will be lost, and the old
one will be restored upon submission.";
print "</font>";
print "<font color=green>";
print "<br> (IN MAIN)the random number generator generated rate3 = $rate3";
print "</font>";
#------------------------------------------------------beginning of traditional saves
if($query->param('rate1') eq ""){ $query->param('rate1',$rate1);}
print $query->hidden('rate1',$rate1);
#------------------------------------------------------end of traditional saves
$rate1 = $query->param('rate1');
#------------------------------------------------------end of traditional restores

#------------------------------------------------------beginning of subroutine based saves
$r1 = \$rate1;
$r2 = \$rate2;
%list = ('rate2',$r2);
&save_variables(%list);
#------------------------------------------------------end of traditional restores

print "<br> enter a number";
print $query->textfield('number','',30,50);
print "<br><font color=blue>Here is a dump, showing the variables CGI.pm is using:</font>";
print $query->dump;
print <<EOF;
<br>
(IN MAIN)
<br>
<font color=red>
rate1 is being changed at every invocation and being saved in the normal
manner. Therefore, it is having the same value at each invocation: $rate1
</font>
<font color=yellow>
<br>Here is rate2, using the save SUBROUTINE which
should have the same value at each invocation: $rate2
</font>
<font color=green>
<br>Meanwhile, the third random number (rate3), not being hidden and saved, was $rate3
</font>
EOF
#=========start
print "<br>",$query->submit;
print $query->end_form;
$number = $query->param('number');
$ans = $number*$rate2;
print "<br>answer = number * <font color=yellow>rate2</font> = $number* <font color=yellow>$rate2</font> = $ans";
print $query->end_html;
#---------------------------------------------
sub save_variables{
  my $var_string = $_[0];
  my $pointer_to_var_string = $_[1];
  my $debug = "false";
  $var_string = "'".$var_string."'";#adjust for future use in CGI.pm
  if($debug eq "true"){
    print "<br> (IN SUBROUTINE)var-string = ",$var_string;
    print "<br> (IN SUBROUTINE) qw{$query->param($var_string)}";
    print "<br> (IN SUBROUTINE)", $query->param($var_string);
    print " <br>   and \ (second variable)= ",$pointer_to_var_string;
  }
  $a = $$pointer_to_var_string;#variable passed by pointer $_[1]
  if($debug eq "true"){
    print "<br>(IN SUBROUTINE)called value \(loc= ",$pointer_to_var_string,") = ",$a;
  }
  if($query->param($var_string) eq '')
		{ $query->param($var_string,$$pointer_to_var_string);#if first time, set
    if($debug eq "true"){
      print "<br>(IN SUBROUTINE) param was blank";
    }
  }#save it
  print $query->hidden($var_string,$$pointer_to_var);
  if($debug eq "true"){
    print "<br>(IN SUBROUTINE) restored value(from query->param) ",
    $query->param($var_string);
  }
  $$pointer_to_var_string = $query->param($var_string);#update value pointed at by $_[1]
  print "<br><br>=====";
}

/ time_stamp:10:52 ,Monday, November 25, 20124
If you wish to enter into a technical discussion (with me or others) via e-mail, and need to use mathematical notation, consider using MathCast (PC's only) which allows pasting equations into e-mails.
If you just wish to write to me directly, without mathematical notation, then please use the form below:
If you wish to comment to C. W. David about this question, assumptions you are making, inconsistencies in the phraseology of the question, objections to the question, etc., etc., etc., you may use this space for that purpose.Please include your e-mail address explicitly. Thank you.
e-mail addresss = at (18.216.145.37)