PREP 2015 Question Authoring - Archived

help with this code?

help with this code?

by Valerio De Angelis -
Number of replies: 6
In this problem I would like the students to give a list of letters as answer, given that the question is another list of letters.
The answer checker produces an error message.
The variable $ans does give the right answer, but apparently I cannot use it in that way in the answer checker.
Any help would be appreciated.
Thank you


# Comments section

DOCUMENT();
loadMacros("PGstandard.pl","PGML.pl");

$i=random(1,6,1);
$j=random(1,5,1);
@A=("A","B","S","T","U","V");
@v=("thinking", "mathematics","college","minimum","commencement");
@set=("{t,h,i,n,k,g}","{m,a,t,h,e,i,c,s}","{c,o,l,e,g}","{m,i,n,u}","{c,o,m,e,n,t}");
$ans=$set[$j];
TEXT(beginproblem());

BEGIN_PGML
Write each set as a list. Do not include repeats. 

[` [$A[$i]] `] is the set of letters in the word _[$v[$j]]_.

The answer to this problem is [$ans]

[` [$A[$i]] =`] [_______]{$ans}
END_PGML

BEGIN_PGML_SOLUTION

*SOLUTION*
END_PGML_SOLUTION
ENDDOCUMENT()
In reply to Valerio De Angelis

Re: help with this code?

by Davide Cervone -
It looks like you are working on an interesting problem. Nice. There are a number of issues that are causing the troubles you are facing.

First, you have not declared a Context(), and so are using the default Numeric context. In that context, braces ({ and }) do not form sets, they are just for grouping and act as another form of parentheses. So you need to switch to one where you can form sets. The only default context for which that is true is the Interval context, so you should start with

Context("Interval");
That will allow answers to be sets.

The second issue is that the value you plan to use in your problem need to be declared in the context. Most contexts come with some predefined name, like the variable x, but you are asking the students to enter letters like m and u; these letter have no meaning in the context, so you will get an error if you use them in your answer (or in student answers).

In order to overcome this, you need to define these letters to have meaning in the problem. The question is what should they mean? Well, since MathObject Set objects only implement finite sets of real numbers, your letters will need to represent real numbers. That can be done by making constants in the context, one for each letter, and assigning them each a different number (so that they will be recognized as distinct values).

Here is one way to do that:

$i = 1;
Context()->constants->are(map {$_ => $i++} ('a'..'z'));
This makes the letters be constants, each representing an integer that is its position in the alphabet. Now you will be able to enter sets like the ones you have in your problem (MathObjects will think of them as sets of numbers, but students will enter them as sets of letters).

There are still some issues, however. The "Entered" column of the results table when a student submits an answer will show the reduced answer, which means the constants will be displayed as their numeric values rather than the letters. You can overcome that by setting a context flag.

Context()->flags->set(formatStudentAnswer => "parsed");
This has the effect of showing the student answer with the constants as letters rather than numbers.

But there is still more to worry about. The context allows operations like addition, multiplication, etc., and functions like sine and cosine, and things like absolute values and intervals, etc. So a student could write {a+c,sin(b)} and not get any error messages. So we should remove the definitions for all those things that are not legal in this type of answer (technically, you are making a new context for "sets of letters", and if you were to write many problems dealing with this, you might actually want to package up the context as a macro file so that you could load it when needed).

I recommend the following changes:

Context()->functions->disable("All");
Context()->variables->clear();
Context()->strings->clear();
Context()->parens->remove("(","[","|");
Context()->operators->clear();
Context()->operators->redefine(","=>{from=>"Interval",using=>','});
The last two lines remove all the operators (like plus and minus), but that would remove the comma as well, and we need that to make the set of letters, so we add that back in the last line.

We are almost finished, but the current setup will produce some poor error messages if the student enters incorrect answers. For one thing, if they enter something that isn't a set, they will get a message telling them to enter "an interval, set, or union". We would like that to read "a set of letters" instead. That can be handled by the following line:

Context()->{cmpDefaults}{Set} = {cmp_class => "Set of letters"};
which overrides the default name for sets in this context.

There is another message that you might want to override. The student might try to enter the original word as is rather than a set, and in that case, since the context says each letter is a number, MathObjects will try to multiply these together via implied multiplication (as it would for things like ax when a is a constant and x is a variable). Since we have removed all the operators (other than comma), including implied multiplication, this would produce an error message about implied multiplication not being allowed. This could be confusing, since they a ring thinking about this as multiplication. So you might want to change that message via the following line:

Context()->{error}{msg}{"Can't perform implied multiplication in this context"} =
    "You must enter sets of individual letters, not whole words";
This asks MathObjects to substitute a different message that is more appropriate to your problem.

One final issue with your code is in the values for $i and $j. In Perl, array indices start at 0, not 1, so your values for these variables should go from 0 to 5 and 0 to 4 rather than 1 to 6 and 1 to 5. One clever way to handle this is to get their values after the arrays have been defined, and have Perl compute the length of the array for you. Here is one way to do that:

@A=("A","B","S","T","U","V");
@v=("thinking", "mathematics","college","minimum","commencement");
@set=("{t,h,i,n,k,g}","{m,a,t,h,e,i,c,s}","{c,o,l,e,g}","{m,i,n,u}","{c,o,m,e,n,t}");
$i = random(0,$#A,1);
$j = random(0,$#set,1);
$ans = $set[$i];
In perl, if you have an array @X, then #$X is the highest index in that array.

So with all that in mind, your problem could be written as follows.


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

Context("Interval");

Context()->{error}{msg}{"Can't perform implied multiplication in this context"} =
     "You must enter sets of individual letters, not whole words";
Context()->functions->disable("All");
Context()->variables->clear();
Context()->strings->clear();
Context()->parens->remove("(","[","|");
Context()->operators->clear();
Context()->operators->redefine(","=>{from=>"Interval",using=>','});
$i = 1;
Context()->constants->are(map {$_ => $i++} ('a'..'z'));
Context()->flags->set(formatStudentAnswer=>"parsed");
Context()->{cmpDefaults}{Set} = {cmp_class => "Set of letters"};

@A = ("A","B","S","T","U","V");
@v = ("thinking", "mathematics","college","minimum","commencement");
@set = ("{t,h,i,n,k,g}","{m,a,t,h,e,i,c,s}","{c,o,l,e,g}","{m,i,n,u}","{c,o,m,e,n,t}");
$i = random(0,$#A,1);
$j = random(0,$#set,1);
$ans = $set[$j];

TEXT(beginproblem());

BEGIN_PGML
Write each set as a list. Do not include repeats. 

[` [$A[$i]] `] is the set of letters in the word _[$v[$j]]_.

The answer to this problem is [$ans]

[` [$A[$i]] =`] [_______]{$ans}
END_PGML

BEGIN_PGML_SOLUTION

*SOLUTION*
END_PGML_SOLUTION
ENDDOCUMENT()

I know there was a lot involved there, but your problem is rather far from the numeric formulas that WeBWorK usually has to process. So it takes some setup to get it to handle this type of situation properly. We certainly hadn't gone over all of that in session, but this was a good opportunity to mention some of the more advanced topics that are needed to make large-scale modifications to the context of a problem.

PS, when you say "The answer checker produces an error message", it would be helpful to provide the actual error message, in case we aren't able to reproduce the problem.

In reply to Davide Cervone

Re: help with this code?

by Valerio De Angelis -
Davide:
thank you for the very detailed and interesting answer, I am going to study it carefully now to make sure I understand all of it. For the moment I am just reporting that the code works fine, producing exactly what I wanted (the student answer is not accepted if the braces are missing, and the order of the elements does not matter), but it does give the following error message:
  • error in options must be an even number of entries: ** main(eval 9051)17Parser::Context::Operators::redefine1256==================
  • Errors parsing PGML:
  • Error parsing answer: Unexpected character ','; see position 3 of formula
after doing a bit of testing I think the problem may be with the  line

Context()->operators->redefine(","=>{from=>"Interval",using=>','});

Maybe it is because the commas have been removed by the previous line it has something to do with counting the number of options on this line? I am just guessing of course...


In reply to Valerio De Angelis

Re: help with this code?

by Davide Cervone -
My fault, the line should have been
Context()->operators->redefine(",",from=>"Interval",using=>',');
without the braces or the initial =>.

Also, you might want to make your answer blank wider, since some of the sets need more space than the rather small one you are using. (This s something most of us could think about, as I've seen lots of blanks that are pretty small for what needs to be entered.)

In reply to Davide Cervone

Re: help with this code?

by Valerio De Angelis -
Thanks, that was very helpful. Because I also wanted to use M in the problem (as in Mississippi), I added the line
Context()->constants->add(M=>100);
and all works fine.
 
In reply to Valerio De Angelis

Re: help with this code?

by Davide Cervone -
In that case, you might want to add ALL the upper-case letters, just in case students use them, so that they won't get unwanted error messages. A map using ('A'..'Z') and starting with $i = 100 would work.