WeBWorK Problems

Intervals or Lists with Strings (and braces)

Intervals or Lists with Strings (and braces)

by Spyro Roubos -
Number of replies: 8
I want a correct answer to be a set of strings; however, it seems like I can only make my answer a "List" without braces.

Ex.:  {Greece, Turkey, USA, Austria}   <--being a correct answer.

I cannot make an interval with strings as it rejects sets with non-real entries.

I know that I can simply make braces around the answer blank, but I'd rather be consistent with how I have students answering these set theory questions (for other ones with numbers, their answer is an interval object where it's expected that they use braces).

Is there a way of doing this?...

Thank you,
Spyro
In reply to Spyro Roubos

Re: Intervals or Lists with Strings (and braces)

by Paul Pearson -
Hi Spyro,

I thought it might be possible to add 'constants' rather than 'strings' to the context, which worked except for the fact that the answer preview shows up using strings while what is entered shows up as decimals (try the code below to see what I mean). What concerns me about the code below is that it appears that the 'reduceConstants' flag is being ignored.

Best regards,

Paul Pearson

##############################

DOCUMENT();
loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"PGcourse.pl",
);
TEXT(beginproblem());
$showPartialCorrectAnswers = 1;


Context("Interval");
Context()->constants->add('alpha'=>Real(pi/2),'beta'=>Real(2.236213));
Context()->flags->set(
reduceConstants => 0,
reduceConstantFunctions => 0,
);
Context()->constants->set( alpha => {keepName => 1},beta => {keepName => 1});


$answer = Compute("{alpha,beta}");

Context()->texStrings;
BEGIN_TEXT
Using set notation, what is the set of the first two letters of the
Greek alphabet?
\{ ans_rule(40) \}
END_TEXT
Context()->normalStrings;

ANS( $answer->cmp(requireParenMatch => 1) );

ENDDOCUMENT();
In reply to Spyro Roubos

Re: Intervals or Lists with Strings (and braces)

by Davide Cervone -
Paul's suggestion works, but there are some improvements that could be made. First, the keepName and requireParenMatch values aren't needed, since these are the default, so they do nothing. Also, the reduceConstants settings are not needed, either. These only affect Formula objects, and (since everything is constant), your answer is a Set, not a Formula, so it is shown numerically (the Set doesn't know how it got its values, only what the values are).

In order to show the student answer symbolically, you need to use formatStudentAnswers => "parsed" so that they are shown as formulas rather than constants.

I'd also recommend adding a TeX rendering to the constants so that their names are displayed better.

My version of the relevant part of Paul's code would be

   Context("Interval");
   Context()->constants->add(
     alpha => pi/2,
     beta  => 2.236213,
   );
   Context()->constants->set(
     alpha => {TeX => '\text{alpha}'},
     beta  => {TeX => '\text{beta}'},
   );
   Context()->flags->set(formatStudentAnswer => "parsed");

   $answer = Compute("{alpha,beta}");

   Context()->texStrings;
   BEGIN_TEXT
   Using set notation, what is the set of the first two letters of the Greek alphabet?
   \{ans_rule(40)\}
   END_TEXT
   Context()->normalStrings;

   ANS($answer->cmp);
Note that there are still some subtle issues, here. First, since alpha and beta are treated as numbers, students could enter things like {sqrt(alpha^2),beta} and still get it right (not that anyone would enter this). Second, you won't get any error message for something like {alpha+1,beta}. Third, if you entered {alpha,x} you'd get an error about your second value not being a number (which is misleading, since you aren't asking the student to enter numbers).

So it works for the most part, but can give some in appropriate error messages.

It is also possible to do as you ask, and use a list of words. But handling the delimiters is a bit tricky (harder than it should be). Here is one approach:

    Context()->strings->add(
      alpha => {caseSensitive => 1},
      beta  => {caseSensitive => 1},
    );
    Context()->parens->set("{" => {type => "List", removable => 0});

    $answer = Compute("{alpha,beta}");

    Context()->texStrings;
    BEGIN_TEXT
    Using set notation, what is the set of the first two letters of the Greek alphabet?
    \{ans_rule(40)\}
    END_TEXT
    Context()->normalStrings;

    ANS($answer->cmp(
      list_type=>"set",
      removeParens=>0,
      implicitList=>0,
      showParenHints=>1
    ));
In this case, we add alpha and beta as words (that must be in lower case -- you may want to change that if you don't care about using "Alpha" for "alpha"), and makes { produce lists, and not be removable. (Usually, if delimiters contain only one element, they are removed, as they are acting like parentheses in that case.)

In the answer checker, we set removeParens => 0 to prevent the outermost list parentheses from being removed from the correct answer automatically (list answers usually don't have delimiters), implicitList => 0 (so that parentheses aren't added to an answer that is a single word), and showParenHints => a so there is a message about problems with the parentheses.

This prevents the problems we had in Paul's case of using the words as numbers. One slight problem here is that if you enter {alpha,x} you are told that entries in a list must be the same type (we would like it to have called it a set rather than a list), and if you enter just {x} it is marked wrong with no message.

Note that you also can't use unions or other set operations in this case, as you could in Paul's case. (Whereas Paul was using numbers to fake words, we are using lists to fake sets, so you will have to decide which set of problems you are willing to accept).

Hope that helps.

Davide

In reply to Davide Cervone

Re: Intervals or Lists with Strings (and braces)

by Spyro Roubos -
Thank you both!  I feel like I understand it a bit better now.

I wound up going with Davide's approach only because I had no way of hiding the numbers that the constants were defined to be.

I have the problem exactly the way I want it with ONE tiny issue.

I've attached a screen shot of the problem.

What I'd like is to turn off the error message that notifies you that you got the 'nth' word incorrect (I know this is the flag 'showEqualErrors  => 0')...
But, I want a different error message to show in a certain case.

If I set ShowEqualErrors to 0, no error messages are shown including the one in my custom answer checker.  ...If I set it to 1, both error messages show.

Is there a quick way of 'on-the-fly' changing an error message?

Thank you both again,
Spyro


Attachment Webwork.png
In reply to Spyro Roubos

Re: Intervals or Lists with Strings (and braces)

by Davide Cervone -
Can you attach your code? It is probably in the way that you provide the error message. The "there is a problem with your nth word" comes from the trapping of errors in general, so you are probably issuing an error message, whereas you want to use one of the other message methods to set the nth warning message directly (so it won't be an untrapped error). But I can't tell you which one you need without looking at your code.
In reply to Davide Cervone

Re: Intervals or Lists with Strings (and braces)

by Spyro Roubos -
# Set Theory - Giving roster form from a chart.

########################################################################

DOCUMENT();      

loadMacros(
   "PGstandard.pl",     # Standard macros for PG language
   "MathObjects.pl",
   #"source.pl",        # allows code to be displayed on certain sites.
   #"PGcourse.pl",      # Customization file for the course
);

# Print problem number and point value (weight) for the problem
TEXT(beginproblem());

# Show which answers are correct and which ones are incorrect
$showPartialCorrectAnswers = 0;

##############################################################
#
#  Setup
#
#

Context("Numeric");


  Context()->strings->add(
      USA => {caseSensitive => 1},
      Greece  => {caseSensitive => 1},
      China  => {caseSensitive => 1},
      Turkey  => {caseSensitive => 1},
      Egypt  => {caseSensitive => 1},
      Iceland  => {caseSensitive => 1},
      Brazil  => {caseSensitive => 1}, 
    );
    Context()->parens->set("{" => {type => "List", removable => 0});

$Country[1] = "Brazil";
$Country[2] = "China";
$Country[3] = "Egypt";
$Country[4] = "Greece";
$Country[5] = "Iceland";
$Country[6] = "Turkey";
$Country[7] = "USA";

# TRAP:  Setting one countries price at exactly $1.25 which is not less than $1.25
$I1 = random(1,7,1);

#Correct Countries
do {$C1 = random(1,7,1);}
while ($C1 == $I1);

do {$C2 = random(1,7,1);}
while (($C2 == $C1) or ($C2 == $I1));

do {$C3 = random(1,7,1);}
while (($C3 == $C2) or ($C3 == $C1) or ($C3 == $I1));

#Question will ask which countries have prices less than $1.25

$answer = Compute("{$Country[$C1], $Country[$C2], $Country[$C3]}");

$commonMistake = Compute("{$Country[$C1], $Country[$C2], $Country[$C3], $Country[$I1]}");

# Run through setting all prices initially to $0
# we do this so we can later differentiate between ones given correct values.

 for ($i = 1; $i <= 7; $i++) {
  $price[$i] = 0;
 }

#Give random but correct prices for Countries C1 - C3
# we want it to be realistic, so the lower bound is $0.70.

$price[$I1] = 1.25;
$price[$C1] = random(0.70, 1.24,0.01);
$price[$C2] = random(0.70, 1.24,0.01);
$price[$C3] = random(0.70, 1.24,0.01);

# Run through the rest (these are all set to zero as default) and
# give the rest prices outside of that range (1.25 or higher).

 for ($i = 1; $i <= 7; $i++) {
  if ($price[$i] == 0) {
              $price[$i] = random(1.26, 1.60,0.01);}
 }


##############################################################
#
#  Text
#
#


Context()->texStrings;
BEGIN_TEXT


The following average price for a can of Coca-Cola is shown below for a few countries: 
$BR
$BR

$BCENTER
\{ begintable(2) \}
\{ row( "Brazil", "$DOLLAR $price[1]") \}
\{ row( "China", "$DOLLAR $price[2]") \}
\{ row( "Egypt", "$DOLLAR $price[3]") \}
\{ row( "Greece", "$DOLLAR $price[4]") \}
\{ row( "Iceland", "$DOLLAR $price[5]") \}
\{ row( "Turkey", "$DOLLAR $price[6]") \}
\{ row( "USA", "$DOLLAR $price[7]") \}

\{ endtable() \}
$ECENTER

$BR
$BR

In roster notation, give the set of countries that have average prices $BBOLD less than $EBOLD ${DOLLAR}1.25. $PAR

\{ans_rule(40)\}

END_TEXT
Context()->normalStrings;

##############################################################
#
#  Answers
#
#

sub mycheck {
  my ($correct, $student, $ansHash) = @_;

if ($student == $Country[$I1]) {

Value->Error("close...but remember $BBOLD less than ${DOLLAR}1.25 $EBOLD"); }



  return $student == $correct;
}


  ANS($answer->cmp(
      list_type=>"set",
      removeParens=>0,
      implicitList=>0,
      showParenHints=>1,
      showLengthHints => 0,
      checker=>~~&mycheck,
     ));


ENDDOCUMENT();        
In reply to Spyro Roubos

Re: Intervals or Lists with Strings (and braces)

by Davide Cervone -
I think this is better handled using answer hints, as follows:
  ANS($answer->cmp(
      list_type=>"set",
      removeParens=>0,
      implicitList=>0,
      showParenHints=>1,
      showLengthHints => 0,
    )->withPostFilter(AnswerHints(
      sub {
         my ($correct,$student) = @_;
         foreach my $name ($student->value) {return 1 if $name == $Country[$I1]}
         return 0;
      } => "Close ... but remember ${BBOLD}less than ${DOLLAR}1.25 $EBOLD",
      sub {
         my ($correct,$student) = @_;
         my %used = ();
         foreach my $name ($student->value) {
           return 1 if $used{$name};
           $used{$name} = 1;
         }
         return 0
      } => "Countries should only be included once in your set"
    ))
  );
While checking this, I realized that my approach didn't produce a warning if you used the same country more than once, so I've included a message for that as well.

Hope that does what you want.

Davide

In reply to Davide Cervone

Re: Intervals or Lists with Strings (and braces)

by Spyro Roubos -
PERFECT!!

That's exactly what I want!  Thank you so much!

I didn't know about that library file 'answerHints.pl'.

Does the postFilter automatically disable / override all messages then?..  (just for future reference if I use that again elsewhere).

Spyro
In reply to Spyro Roubos

Re: Intervals or Lists with Strings (and braces)

by Davide Cervone -
Does the postFilter automatically disable / override all messages then?
No, AnswerHints won't change the message if there already is one (unless you tell it to). See the answerHints.pl POD documentation for details.