WeBWorK Problems

percent as a unit

percent as a unit

by Alex Jordan -
Number of replies: 2
I added the following in lib/Units.pm:

+ '%' => {
+ 'factor' => 0.01,
+ },


So that in a problem I can make something like

NumberWithUnits(5,"%/min");

I find that this compiles, and a student could type "5 %/min" or "0.05 min^-1" and it's counted correct. However the Answer Preview does not show the "%" sign.
  • Is anyone aware of anything very bad about what I'm doing here? Is using '%' as a key in lib/Units.pm safe?
  • Is there a way to make the TeX processing for a unit differ from the plain text string? Could I somewhere declare '%' to output as '\%' for the Answer Preview?

Note: I do know about contextPercent.pl and I use it all the time. But here I'm trying to get the "%/min" into the answer the student must type.

In reply to Alex Jordan

Re: percent as a unit

by Alex Jordan -
I added the code below to the PG problem, which modifies a subroutine in Parser::Legacy::ObjectWithUnits.

It appears to let me make
$p=NumberWithUnits(5,'%/min');
which:
  • outputs fine in a problem statement using images
  • outputs fine in a problem statement using MathJax
  • outputs fine in a problem statement in hardcopy
  • outputs fine in the correct answer field in HTML
  • outputs fine in the correct answer field in hardcopy
  • lets "5 %/min" work as correct input from a student
  • lets "0.05 min^-1" work as correct input from a student
I believe I have learned from Davide that I should not be modifying a subroutine from Parser::Legacy::ObjectWithUnits from within a PG problem, because it will have effects that extend beyond this one problem. So I might make the corresponding (similar, not exactly the same) changes to pg/lib/Parser/Legacy/NumberWithUnits.pm. If I change both NumberWithUnits.pm and Units.pm as mentioned in my earlier post, does it provide an all-around valid way to get "%" to count as a unit? If so I might turn this into a pull request, unless it is bad form to be making new edits in a "Legacy" module.

Additionally, I have discovered that a student cannot submit something like "0.05/min" for an answer, even though "0.05 min^-1" is OK. The module doesn't recognize denominator-only units. I might investigate giving that functionality too.


package Parser::Legacy::ObjectWithUnits;

sub TeXunits {
my $units = shift;
$units =~ s/~~^~~(?([-+]?~~d+)~~)?/^{$1}/g;
$units =~ s/~~*/~~~~,/g;
$units =~ s/%/~~~~%/g; # new line
return '{\rm '.$units.'}' unless $units =~ m!^(.*)/(.*)$!;
my $displayMode = $envir{displayMode}; # modified line
return '{\textstyle\frac{'.$1.'}{'.$2.'}}' if ($displayMode eq 'HTML_tth');
return '{\textstyle\frac{\rm\mathstrut '.$1.'}{\rm\mathstrut '.$2.'}}';
}

package main;

In reply to Alex Jordan

Re: percent as a unit

by Davide Cervone -
Sorry for the long delay in getting back to you. I don't really have time to answer WeBWorK questions when I'm teaching.

The issue is in the TeXunits method of the NumberWithUnits object (lines 84 through 92). This doesn't escape all possible TeX special characters (since all the units were alphabetical at the time it was written).

One solution for your case would be to use

sub TeXunits {
  my $units = shift;
  $units =~ s/\^\(?([-+]?\d+)\)?/^{$1}/g;
  $units =~ s/\*/\\,/g;
  $units =~ s/%/\\%/g;   # escape percent signs
  return '{\rm '.$units.'}' unless $units =~ m!^(.*)/(.*)$!;
  my $displayMode = WeBWorK::PG::Translator::PG_restricted_eval(q!$main::displayMode!);
  return '{\textstyle\frac{'.$1.'}{'.$2.'}}' if ($displayMode eq 'HTML_tth');
  return '{\textstyle\frac{\rm\mathstrut '.$1.'}{\rm\mathstrut '.$2.'}}';
}
but this just fixes it for percents. If you want to handle all possible special characters, then something like this might work:

my %escape = (
  '"'  => '{\ttfamily\char34}',
  "\#" => '{\ttfamily\char35}',
  '$'  => '\$',
  '%'  => '\%',
  '&'  => '\&',
  '<'  => '{\ttfamily\char60}',
  '>'  => '{\ttfamily\char62}',
  '\\' => '{\ttfamily\char92}',
  '^'  => '{\ttfamily\char94}',
  '_'  => '\_',
  '{'  => '{\ttfamily\char123}',
  '|'  => '{\ttfamily\char124}',
  '}'  => '{\ttfamily\char125}',
  '~'  => '{\ttfamily\char126}',
);

sub TeXunits {
  my $units = shift;
  $units =~ s/(["\#\$%&<>\\^_\{|\}~])/$escape{$1}/eg;
  $units =~ s/\^\(?([-+]?\d+)\)?/^{$1}/g;
  $units =~ s/\*/\\,/g;
  return '{\rm '.$units.'}' unless $units =~ m!^(.*)/(.*)$!;
  my $displayMode = WeBWorK::PG::Translator::PG_restricted_eval(q!$main::displayMode!);
  return '{\textstyle\frac{'.$1.'}{'.$2.'}}' if ($displayMode eq 'HTML_tth');
  return '{\textstyle\frac{\rm\mathstrut '.$1.'}{\rm\mathstrut '.$2.'}}';
}
(This was cribbed from the PGML.pl file, so it should work, but I haven't tested it.) I suppose such an escaping function should be more readily available rather than having to redo it every time it is needed.

Hope that helps.

Davide