PREP 2015 Question Authoring - Archived

Using CPAN modules in WeBWorK problems

Using CPAN modules in WeBWorK problems

by Jan Hlavacek -
Number of replies: 6

In one of my homework problems, I had to compute a sum of an array several times. I ended up writing my own sum subroutine, but there is a sum available in List::Util. Can I just do use List::Util qw(sum);? If so, where in the problem would I put it? If not, is there any other way how to use modules?

In reply to Jan Hlavacek

Re: Using CPAN modules in WeBWorK problems

by Gavin LaRose -
Hi Jan,

There are a couple of things going on here: first is whether the object you're working with is a Perl list (array) or a MathObjects List. A second is that I think that use is a trapped function call, so you won't be able to call it in the homework problem (this is because WeBWorK problems run in a tightly controlled environment for security purposes).

One option is to have a local library of functions in which we include a sum function that we can then use multiple places. For example, if we are working with Perl arrays and aren't worried about error trapping, we might define the subroutine

    sub sumlist {
        my $sum = 0;  
        foreach ( @_ ) { $sum += $_; }
        return $sum;
    }

in a file called localMacros.pl, located in the templates/macros in the course directory. Then by including localMacros.pl in the loadMacros() at the start of the problem file we gain access to our sumlist function.

Also if we're just in Perl, a cryptic and possibly not overly efficient sum function is one line, so that may be sufficient for our purposes:

    my $sum = 0; map { $sum += $_ } @array;

Both of these don't really address the original question: how we get access to other modules. The short answer to that is, I think, that it's not straightforward but that there are a lot of predefined utilities in the PG codebase. In some cases (I don't think sum() is one of them) something that will do what we need may be located in one of those. A Google search for "WeBWorK blah function" might find what we need if that's the case.

Does that help?
Gavin

In reply to Jan Hlavacek

Re: Using CPAN modules in WeBWorK problems

by Paul Pearson -
Hi Jan,

First of all, if you have pg code, please include all of it in your post so that we have something specific to work from.

If you are trying to find the sum of all elements in an array, you can use a for loop in Perl to do that.  For instance, 

$sum = 0;
foreach my $i (0..$#a) { 
  $sum += $a[$i]; 
}

will add all of the elements in the array @a and store the value of the sum in the scalar $sum.  For more details and a few different ways to compute the sum, see the code below my signature.

I think the creators of webwork created a "safe box" in which certain Perl macros are allowed to run and others are not.  What that means is that a problem author does not have the ability to load Perl packages directly (unless they also rewrite some of the pg and webwork code to allow it).  This is a good safety feature, since you would not want a problem author or some malicious hacker to be able to execute Perl commands that could disable or corrupt your webwork server from a .pg homework file.

Best regards,

Paul Pearson

################  BEGIN PG CODE ######################

DOCUMENT();

loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"PGML.pl",
"PGcourse.pl",
);

TEXT(beginproblem());

$showPartialCorrectAnswers = 1;

Context("Numeric");

@a = (); # create an empty Perl array
# add 5 random numbers to the Perl array @a
foreach my $i (0..4) {
  $a[$i] = non_zero_random(-5,5,1);
}

$sum = 0; # initial value for the sum

# this is one way that works
#foreach my $i (0..$#a) { # use $#a to get the length of the array minus 1
#  $sum += $a[$i]; # use += to add to the existing value of $sum
#}

# this is a more "highfalutin" way that also works
foreach my $e (@a) { # for each element $e in the array @a
  $sum += $e; # use += to add the value of $e to the value of $sum
}


$a_string = join(',' , @a);

sub sum {
my @data_list = @_;
my $total = 0;
foreach ( @data_list  )  {
 $total=$total + $_;
}
return( $total );
}

$s = sum(@a);


################################
#  Main text

BEGIN_PGML
The sum of the list [` [$a_string] `] is [` [$sum] `].

You could also make a subroutine to calculate sums and get [` [$s] `] for the sum.
You could put the [| sum() |] subroutine into the local customization macro file [| PGcourse.pl |], load the macro file, and use it in a problem.
END_PGML


COMMENT('MathObject version. Uses PGML.');

ENDDOCUMENT();

################### END PG CODE #####################
In reply to Paul Pearson

Re: Using CPAN modules in WeBWorK problems

by Jan Hlavacek -
Thanks, that is very helpful, especially the part about the local customization file.

I was not that concerned about the sum itself, as it is an easy function to write, but the general possibility of using functions defined elswhere. I suspected that importing any old module may not be allowed for security reasons, thus the question.

Here is the file that lead to this question:

# DESCRIPTION
# Standard deviation of a list
# written by Jan Hlavacek (jhlavace@svsu.edu)
# ENDDESCRIPTION

## DBsubject('Statistics')
## DBchapter('')
## DBsection('')
## KEYWORDS('standard deviation')
## TitleText1('')
## EditionText1('')
## AuthorText1('')
## Section1('')
## Problem1('')
## Author('Jan Hlavacek')
## Institution('SVSU')

DOCUMENT();

loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"PGML.pl",
"PGcourse.pl",
);
Context("Numeric");

# Define variables here:

sub sum
{
 local($s);
 $s = 0;
 foreach(@_)
 {
 $s = $s + $_;
 }
 $s;
}

$length = random(5,9,1);
$min = random(0,10,1);
$span = random(1,10,1);
@data = map { $min + random(0,100*$span)/100 } (1..$length);
$mean = sum(@data)/$length;
@devs = map {$_ - $mean} @data;
@sqdevs = map {($_)**2} @devs;
$sumsqdevs = sum( @sqdevs );
$var = $sumsqdevs/($length-1);
$sd = sqrt($var);

$datarep = join(', ', @data);
$datasum = join('+',@data);
$calcdevs = join('\\', map { "x_{$_} - \bar{x} &= $data[$_-1] - $mean = $devs[$_-1]"} 1 .. ($#data+1));
$squaredevs = join('\\', map { "($devs[$_])^2 =& $sqdevs[$_]" } 0 .. $#devs);

# Actual problem goes here:
TEXT(beginproblem());
BEGIN_PGML
Find the standard deviation of the following sample:

>>[`[$datarep]`]<<

The standard deviation of the sample is [_______]{$sd}.
END_PGML

# Solution:
BEGIN_PGML_SOLUTION
*SOLUTION*
First we calculate the mean of the data:

[``\bar{x} = \frac{\sum x}{n} = \frac{[$datasum]}{[$length]} = [$mean]``]

Then we calculate the deviations:

[``\begin{align*}[$calcdevs]\end{align*}``]

We square all the deviations and add the squares together:

[``\begin{align*}[$squaredevs]\\\hline\sum(x-\bar{x})^2 =&[$sumsqdevs]\end{align*}``]

Since it is a standard deviation of a _sample_, we divide the sum of squared deviations by [`n-1`] to get the variance:

[``s^2 = \frac{\sum (x - \bar{x})^2}{n-1} = \frac{[$sumsqdevs]}{[$length] - 1} = [$var]``]

Finally, we take the square root of the variance to find the standard deviation:

[``s = \sqrt{s^2} = \sqrt{[$var]} = [$sd]``]

END_PGML_SOLUTION

ENDDOCUMENT();

In reply to Jan Hlavacek

Re: Using CPAN modules in WeBWorK problems

by Paul Pearson -
Hi Jan,

I think you should be using the PGstatisticsmacros.pl and promoting your Perl scalars to MathObjects.  See the code below for an example.

Best regards,

Paul Pearson

################### BEGIN PG CODE ####################

DOCUMENT();  

loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"PGstatisticsmacros.pl",
"PGML.pl",
"PGcourse.pl",
);

TEXT(beginproblem());

Context("Numeric")->flags->set(tolerance => 0.01, tolType=>"absolute");

$n = Compute( 10 );

@data = urand(2, 4.5, $n, 2); # mean, sd, numpoints, numdigits

$mean = Compute( stats_mean( @data ) );
$sd = Compute( stats_sd( @data ) );
$sum = Compute( $mean * $n );

$data_string = join(', ' , @data);

BEGIN_PGML
The data [` [$data_string] `] has 

+ number of points = [______]{$n}

+ mean = [________]{$mean}

+ standard deviation = [________]{$sd}

+ sum = [_______]{$sum}

For more details on the statistics macros, see [@ htmlLink("https://github.com/openwebwork/pg/blob/master/macros/PGstatisticsmacros.pl","the macro file") @]*
END_PGML

COMMENT('MathObject version. Uses PGML.');

ENDDOCUMENT();

################### END PG CODE ####################
In reply to Paul Pearson

Re: Using CPAN modules in WeBWorK problems

by Jan Hlavacek -
Nice, thank you. I will try to rewrite the whole problem, including solution, with MathObjects this week.