WeBWorK Main Forum

Checking lists of strings and checking String objects

Checking lists of strings and checking String objects

by Robin Cruz -
Number of replies: 1

I thought I knew how to do this but I cannot get this to work.  I have two difficulties:

1) If I make the single answers into Strings, even when I've added the string to the context, the answer is scored incorrect and the message says that "AM" is not in the context.

2) The string list evaluator is not seeing the second item in the correct answer list. 

This would be a lot easier if I could add custom units to NumberwithUnits--something to add later? 

Anyway, here is the code I've tried.   The students look at a graph of temperature over time (hours after midnight) and enter the time a particular temperature occurs. Answers are to be rounded to the nearest integer hour.

Thanks -- rac

---------------------------------------------------------------------------------------

#Context()->strings->add("12 AM","1 AM"=>{},"2 AM"=>{},"3 AM"=>{},
#                        "4 AM"=>{},"5 AM"=>{},"6 AM"=>{},"7 AM"=>{},
#                        "8 AM"=>{},"9 AM"=>{},"10 AM"=>{},"11 AM"=>{},
#                        "12 PM"=>{},"Noon"=>{alias=>"12 PM"},
#                        "1 PM"=>{},"2 PM"=>{},"3 PM"=>{},"4 PM"=>{},
#                        "5 PM"=>{},"6 PM"=>{},"7 PM"=>{},"8 PM"=>{},
#                        "9 PM"=>{},"10 PM"=>{},"11 PM"=>{});

$units_a = "AM";
$ta = $x0;     #$x0 is hours after midnight
if ($ta>=12)
  {
   $units_a = "PM";
   if ($ta>12) {$ta=$ta-12;}
  }

#$ans_a = String("$ta $units_a");
#ANS($ans_a->cmp);

$ans_a = "$ta $units_a";
ANS(str_cmp($ans_a));

$units_b ="AM";
$t1 = $x1;   #x1 and x2 are hours after midnight
$t2 = $x2;
if ($t2>=12)
  {
   $units_b = "PM";
   if ($t2>12) {$t2=$t2-12;}
  }

#$ans_b = List(String("$t1 AM"),String("$t2 $units_b"));
#ANS($ans_b->cmp);

@ans_b = ("$t1 AM","$t2 $units_c");
ANS(std_str_cmp_list(@ans_b));

 

In reply to Robin Cruz

Re: Checking lists of strings and checking String objects

by Davide Cervone -
Robin, you always have interesting problems. The problem, here, is subtle, and not something you would have been able to figure out on your own. It has to do with how the patterns are handled for matching against the various objects in the MathObject Context, like the strings, operators, and so on. most of these are folded into one large pattern, but there are a couple of special ones, like the pattern for a numeric constant. It turns out that the numeric constant pattern is handled before matching against the other objects, and since your strings start with numbers, the parser was matching the number first, and that left only the "AM" or "PM" as the rest. Since there was no definition for "AM" or "PM", WeBWorK complained about it.

There are several possible solutions to this. The first is to disable the numeric checking in the context by doing the following:

    Context()->{pattern}[number} = '^$';
    Context()->update;
This will make the number pattern be blank, which will never match anything (since a match it attempted only when there actually is something to match against). This should allow your time strings to be matched. Note, however, that by default your context also has definitions for all the operators (+, - , *, etc), functions (sin(), cos(), etc), constants (e, pi), and so on, so student could type these and get strange error messages. So you might want to remove them, as follows:
    Context()->strings->clear;
    Context()->constants->clear;
    Context()->variables->clear;
    Context()->functions->clear;
    Context()->operators->clear;
    Context()->parens->clear;
before adding your time strings to the context. This will make it impossible to type anything but time strings. However, you will still get some bad error messages (things like "Unexpected character '+'" if you type a plus). To solve that problem, you might use
    Context()->{error}{msg}{"Unexpected character '%s'"} =
         "Your answer doesn't look like a time value";
    Context()->{error}{msg}{"'%s' is not defined in this context"} =
         "Your answer doesn't look like a time value";
to translate those messages into a less precise, but perhaps less confusing, message.

Since you are using a List later on, you will want to allow the comma operator, so you can redefine it using

    Context()->operators->redefine(',',from=>"Numeric");
Finally, you might want to make aliases like "1AM"=>{alias=>"1 AM"} so that the space can be left out, since the space is required in the current form.

An alternative approach would be to declare a variable called "AM" and one called "PM", so that "1 AM" would really be "1*AM". You could adjust the output of the implicit multiplication using

    Context()->operators->set(" "=>{string=>" ",TeX=>"\ ",perl=>"*");
so that it would display properly in the preview and hardcopy. This would solve the problem of allowing spaces, and would allow mathematical operations in the hour (as in "5+3 am"), but it would not handle switching from AM to PM correctly, and you could do unusual things like "3 AM^2" or "AM 3" or just plain "AM" and not get an error message. You could also get error messages that suggest you should enter a formula (since the AM is a variable), and that might be confusing.

You suggest that what you want to do is set a new Unit for this. That is a nice idea, but I don't think that will quite work, because the way the unit library works by using multiplicative factors to convert the student's and professor's answers to a common unit, but the AM/PM difference is additive, not multiplicative.

There really needs to be a time-value context, so I'll put that on my list of things to work on .

Davide