WeBWorK Problems

context Limited Numeric

context Limited Numeric

by Kenneth Appel -
Number of replies: 9
Davide,
One of the things that middle school texts seem to insist on is that
a decimal that is less than 1 be written with an initial 0. Certainly, at the
college level we are quite satisfied with answers of the form .3, when 0.3
is mandatory in some middle school texts.

If I want to modify the Limited Numeric context to reject answers of
the form .xxx with the appropriate comment in the answer box is it necessary
to make the modification of context Limited Numeric a subroutine or is there
an easy way of just putting in an extra part to the call to the context. I am
quite sure that one can do it if one just wants to reject the .xxx answer, but
what about getting in the explanation of the error.
Ken
In reply to Kenneth Appel

Re: context Limited Numeric

by Davide Cervone -
There are several approaches to handling this. Probably the best one is to use the Context's NumberCheck flag to set up a number checking routine. This will be called every time a number is parsed, and so can be used to check that it had the leading zero. This can be used wih any context, but I'll illustrate it in the LimitedNumeric one.

Here's an example:


  Context("LimitedNumeric");

  Context()->flags->set(
    NumberCheck => sub {
      my $self = shift;
      $self->Error("Decimals must have a number before the decimal point")
        if $self->{value_string} =~ m/^~~./;
    }
  );

  ANS(Real(0.23)->cmp);

Here the number checker returns an error if the value entered by the student starts with a decimal point with no zero. Note that negative numbers also are checked by this, since the negation is performed by an operation, and is not considered part of the number itself. Also note the use of ~~ where perl would have used a back-slash in the match string. That is a peculiarity of the PG parser (since it tried to preserve backslashes within your problem). If you were to move the subroutine to a macro file for use by more than one problem, you would need to convert the double-tilde to a back-slash.

Davide

In reply to Davide Cervone

Re: context Limited Numeric

by Kenneth Appel -
Davide,
Thanks.
I think that I need to know more about context calls. For instance, what is
the difference between the following forms of context introduction
Context("LimitedNumeric")

Context()->texStrilngs

I tried to figure out how to put what you suggested into the macro file, and, as
you can probably guess (because you are getting this message) failed.
I did not know if I could put in separate context calls, but assumed not because
the second could cancel the first. Thus I tried this (first text of my stuff, then the
problem, then the error message=head1 NAME

contextLeadingZero,pl
=head1 DESCRIPTION

Check to make sure that a decimal starts with an integer (ie require
leading 0's)
=cut
Context("LimitedNumeric");

Context()->flags->set(
NumberCheck => sub{
my $self = shift;
$self->Error("Decimals must have a number before the decimal point")
if $self-{value_string} =~ m^\./;
}
};

For some reason I can't get mouse copy to work so I will have to tell you the
important things in the program

in LoadMacros

"contextLimitedNumeric.pl",
"contextLeadingZero.pl"

then at the end
Context("LeadingZero");
ANS ...

The error message was
Error detected while loading [PG}/macrose/contexLeadingZero.pl
Search pattern not terminated at line 17 [the end of load macros - );] chunk 1

What should I have done?
Ken

In reply to Kenneth Appel

Context calls

by Davide Cervone -
What is the difference between the following forms of context introduction?
    Context("LimitedNumeric");
    Context()->texStrings;

The first sets the context to a copy of the LimitedNumeric context. Even if you have used LimtedNumeric context earlier in the problem, you get a fresh copy (any changes you may have made to it earlier are lost. The call returns a reference to the copy, so you could use that to reference the context. So for example

    $context = Context("LimitedNumeric");
    $context->flags->set(reduceConstants=>0);
or more compactly
    Context("LimitedNumeric")->flags->set(reduceConstants=>0);

The second form uses Context() with no name. This returns a reference to the context that is currently in use. So

    Context("LimitedNumeric");
    Context()->flags->set(reduceConstants=>0);
is equivalent to the first example above.

Your command

    Context()->texStrings;
calls the texStrings method of the current context, whereas
    Context("LimitedNumeric")->texStrings;
would first set the current context to a copy of the LimitedNumeric context (discarding the previously active context), and then call the texStrings method of that copy.

Hope that clarifies it.

Davide

In reply to Kenneth Appel

Re: context Limited Numeric

by Davide Cervone -
The error message says that the search patter is not terminated in line 17. That suggests that there is a problem with the regular expression used on that line. The line in question seems to be
    if $self-{value_string} =~ m^\./;
and there is a typo in the match patter (the stuff after the "m"). You are missing the slash that initiates the match pattern, and so the caret is being used as the delimiter, and since there isn't a second caret, Perl is complaining that you haven't closed the regular expression for the match. You need to use m/^\./ instead.

(Note that you also have an error earlier, as it should be $self->{value_string} not $self-{value_string}. The greater-than sign is missing.)

Davide

In reply to Kenneth Appel

Re: context Limited Numeric

by Davide Cervone -
Once you correct the typos above (you should really figure out how to get cut and past to work), there is still going to be a problem, as you have not actually created a context called LeadingZero. Having a file called contextLeadingZero.pl is not sufficient (the name of the file doesn't matter).

To create a context, you usually make a copy of an existing one and change it. To store the context where Context() can find it by name, save the copy as $context{name}. For example,

    $context{LeadingZero} = Parser::Context->getCopy("LimitedNumeric");
Then make changes to $context{LeadingZero}, and laer you can access the context using
    Context("LeadingZero");
just like any standard context.

So your contextLeadingZero.pl file might contain

    loadMacros("contextLimitedNumeric.pl");

     $context{LeadingZero} = Parser::Context->getCopy("LimitedNumeric");
     $context{LeadingZero}->flags->set(
        NumberCheck => sub {
          my $self = shift;
          $self->Error("Decimals must have a number before the decimal point")
            if $self->{value_string} =~ m/^\./;
        }
      );
and your problem file could then do
    loadMacros("contextLeadingZero.pl");

    Context("LeadingZero");
    ....

Note, however, that this is not as general a solution as you might like, as it forces the context to be the LimitedNumeric one. It might be that you want to force leading zeros for some other context. It might be better to make your macro file be one that allows you to modify any context (by setting the NumberCheck flag).

Here's one (unchecked) possibility:

    sub context::LeadingZero {
      my $context = shift || Parser::Context::current;  # context is argument or current context.
      $context->flags->set(NumberCheck => \&context::LeadingZero::NumberCheck;
    }

    sub context::LeadingZero::NumberCheck {
      my $self = shift;
      $self->Error("Decimals must have a number before the decimal point")
        if $self->{value_string} =~ m/^\./;
    }
This defines a routine that modifies a given context (or the current one if none is given) to require leading zeros. You would use it in a problem as follows:
    loadMacros(
      "contextLimitedNumeric.pl",
      "contextLeadingZero.pl",
    );

    Context("LimitedNumeric");
    context::LeadingZeros;
    .
    .
    .
    ANS(Real(0.23)->cmp);

Hope that helps.

Davide

In reply to Davide Cervone

Re: context Limited Numeric

by Kenneth Appel -
Davide,
I decided that I would try first to get the restricted version to work. It is my
impression that I wrote the context as you suggested, but my proofreading is so
dreadful that I can't guarantee that. In any event, in the file contextLeadingZero.pl
is now stored:

[root@homework macros]# more contextLeadingZero.pl
################################################################################

=head1 NAME

contextLeadingZero.pl - implements contextLimitedNumeric with the additional
feature that decimals need integer before decimal pt

=head1 DESCRIPTION

=cut


loadMacros("contextLimitedNumeric.pl");

$context{LeadingZero} = Parser::Context->getCopy("contextLimitedNumeric");
$context{LeadingZero}=>flags->set(
NumberCheck => sub {
my $self = shift;
$self->Error("Decimals must have a number before the decimal point")
if $self->{value_string} =~ m/^\./;
}
);
I embedded this in the problem as shown below (with error message preceding
file listing.

set1: Problem 3

This set is visible to students.

WeBWorK Error

WeBWorK has encountered a software error while attempting to process this problem. It is likely that there is an error in the problem itself. If you are a student, report this error message to your professor to have it corrected. If you are a professor, please consult the error output below for more information.

Error messages

Error detected while loading [PG]/macros/contextLeadingZero.pl: Can't locate object method "set" via package "flags" (perhaps you forgot to load "flags"?) at line 24 of [PG]/macros/contextLeadingZero.pl, chunk 1 Died within main::compile_file called at line 308 of (eval 1122) from within main::loadMacros called at line 6 of [TMPL]/msprobs/mn+o5-1inter13b.pg

Error details

 Problem3
ERROR caught by Translator while processing problem file:msprobs/mn+o5-1inter13b.pg
****************
Error detected while loading [PG]/macros/contextLeadingZero.pl:
Can't locate object method "set" via package "flags" (perhaps you forgot to load "flags"?) at line 24 of [PG]/macros/contextLeadingZero.pl,  chunk 1
 Died within main::compile_file called at line 308 of (eval 1122)
 from within main::loadMacros called at line 6 of [TMPL]/msprobs/mn+o5-1inter13b.pg

****************

------Input Read
1 #DESCRIPTION
2 ##Type of
3 #ENDDESCRIPTION
4
5 DOCUMENT();
6 loadMacros(
7 "PGstandard.pl",
8 "PGchoicemacros.pl",
9 "PGgraphmacros.pl",
10 "MathObjects.pl",
11 "compoundProblem.pl",
12 "contextCurrency.pl",
13 "unionLists.pl",
14 "unionMacros.pl",
15 "contextLimitedNumeric.pl",
16 "contextLeadingZero.pl",
17 );
18 Context()->texStrings;
19 TEXT(beginproblem());
20 $showPartialCorrectAnswers = 1;
21 $a=random(61,69,1);
22 $b=random(31,39,1);
23 $c=random(1,9,1);
24 $ans1= $a/100;
25 $ans2=$b/100;
26 $ans3=$c/100;
27 $ans4=.19;
28 $ans5=.78;
29 Context()->texStrings;
30 BEGIN_TEXT
31 $PAR
32 Write each as a decimal
33 $PAR
34 \( \frac{$a}{100}=\) \{ans_rule(3)\}
35 $PAR
36 \( \frac{$b}{100}= \) \{ans_rule(3)\}
37 $PAR
38 \( \frac{$c}{100}= \) \{ans_rule(2)\}
39 $PAR
40 nineteen hundredths =\{ans_rule(3)\}
41 $PAR
42 seventy-eight hundredths =\{ans_rule(3)\}
43
44 END_TEXT
45 Context("LeadingZero");
46 #note, this should be modified for .xxx
47 &ANS(Real($ans1)->cmp);
48 &ANS(Real($ans2)->cmp);
49 &ANS(Real($ans3)->cmp);
50 &ANS(Real($ans4)->cmp);
51 &ANS(Real($ans5)->cmp);
52
53 ENDDOCUMENT()
Was I supposed to do something about the flags other than what was in the
new context?

Ken,

PS I spent two days at the beach finally reading "Programming Perl" in
more detail (It is amazing how much one can get away with using ordinary
webwork problems with a limited knowledge of Perl) But this one still
eludes me.
In reply to Kenneth Appel

Re: context Limited Numeric

by Davide Cervone -
This time the typo was mine. The error is
     $context{LeadingZero}=>flags->set( 
should be
    $context{LeadingZero}->flags->set( 
(the equal should be a dash). This error was in my original post, which I have edited so that it is now correct. Sorry about the error.

Davide

In reply to Davide Cervone

Re: context Limited Numeric

by Kenneth Appel -
Davide,
There seems to still be something wrong. I made the change.
The old error message was

Error detected while loading [PG]/macros/contextLeadingZero.pl: Can't locate object method "set" via package "flags" (perhaps you forgot to load "flags"?) at line 24 of [PG]/macros/contextLeadingZero.pl, chunk 1 Died within main::compile_file called at line 308 of (eval 1122) from within main::loadMacros called at line 6 of [TMPL]/msprobs/mn+o5-1inter13b.pg

Now the error message is

Error detected while loading [PG]/macros/contextLeadingZero.pl: Can't call method "flags" on an undefined value at line 24 of [PG]/macros/contextLeadingZero.pl, chunk 1 Died within main::compile_file called at line 308 of (eval 1118) from within main::loadMacros called at line 6 of [TMPL]/msprobs/mn+o5-1inter13b.pg

Ken
In reply to Kenneth Appel

Re: context Limited Numeric

by Davide Cervone -
The line
    $context{LeadingZero} = Parser::Context->getCopy("contextLimitedNumeric");
should be
    $context{LeadingZero} = Parser::Context->getCopy("LimitedNumeric");
(no "context" in the name of the context). As you originally have it, you were asking for a non-existent context, and got a null return value. When you used it on the next line, perl complained about calling the flags method on an undefined reference.

Davide