WeBWorK Problems

arrays in PGML

arrays in PGML

by Valerio De Angelis -
Number of replies: 11
In the following problem, I want to display four pictures in tabular form, so I tried to use (in PGML) [: Array( [$f0]*, [$f1]* ; [$f2]* , [$f3]* ) :]
but it did not work. I do not get an error message, but instead of a picture I get the code that defines it. So I just wrote

A [$f0]* [: quad quad :] B [$f1]*

C [$f2]* [: quad quad :] D [$f3]*

and this does the job well for the purpose of this problem, but I wonder how one could use Array in this situation.

A (maybe related?) problem I had in writing this problem was that in PGML, I could not display the images as [$fig[0]] [$fig[1]] [$fig[2]] [$fig[3]]
but had to instead define and use [$f0] [$f1] [$f2] [$f2]. I am guessing that it has something to do with how PGML interprets the square brackets.

Thanks for any information.




DOCUMENT();

loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"parserPopUp.pl",
"PGML.pl",
"PGcourse.pl"
);
 
TEXT(beginproblem());

$refreshCachedImages=1;
 
$fig[0]=image("H1_1_1.png"); #the png files contain pictures of graphs
$fig[1]=image("H1_1_2.png");
$fig[2]=image("H1_1_3.png");
$fig[3]=image("H1_1_4.png");

$k = random(0,3,1); # $k, $a1,$a2,$a3 are a cyclic permutation of 0,1,2,3
$a1=($k+1)%4;
$a2=($k+2)%4;
$a3=($k+3)%4;


$f0=$fig[$k];
$f1=$fig[$a1];
$f2=$fig[$a2];
$f3=$fig[$a3];

$e=(1-$k)%4; # this selects fig[1] as the correct answer


@letter = ("A", "B", "C", "D");

$popup = PopUp(["?","A","B","C","D"], $letter[$e]);


BEGIN_PGML
Which picture A-D does *not* describe a function?
[$popup->menu() ]*

A [$f0]* [: quad quad :] B [$f1]*

C [$f2]* [: quad quad :] D [$f3]*

END_PGML


install_problem_grader(~~&std_problem_grader);
$showPartialCorrectAnswers = 0;

ANS( $popup->cmp() );

BEGIN_SOLUTION
$letter[$e] does not describe a function, because we can draw a vertical line that
intersects the graph twice.
END_SOLUTION



ENDDOCUMENT();
In reply to Valerio De Angelis

Re: arrays in PGML

by Gavin LaRose -
Hi Valerio,

If you're just trying to display four images in a tabular form, I would use a layout table instead of trying to put it into a math equation. I think something like the following would work.

loadMacros("niceTables.pl");

@figs = ( image("H1_1_1.png"), 
          image("H1_1_2.png"),
          image("H1_1_3.png"), 
          image("H1_1_4.png") );

BEGIN_PGML
[@ LayoutTable(
  [ [ $figs[0], $figs[1] ],
    [ $figs[2], $figs[3] ] ] ) @]*
END_PGML

More information about the LayoutTable command is at http://webwork.maa.org/wiki/Tables.

Gavin
In reply to Valerio De Angelis

Re: arrays in PGML

by Davide Cervone -
Gavin has already given you a good solution, but I wanted to point out that, in PGML, [: :] produces typeset mathematics using algebra notation as the input. The Array() function is for creating arrays of mathematics. The algebra notation does not include a mechanism for including images in the displayed mathematics, and even if it did, that would not be an appropriate means of doing the table you are looking for. The LayoutTable() that Gavin proposes is the right way to do it.

Also, it is not really appropriate to use mathematics for spacing purposes outside of mathematics, so [: quad quad :] is not really very good either. You could have used pre-formatted layout if you wanted spacing to count. That is initiated by a colon with three spaces.
In reply to Davide Cervone

Re: arrays in PGML

by Valerio De Angelis -
Thanks to Gavin and Davide for their replies. If I understand well, Array() versus LayoutTable() is like \begin{array} \end{array} vs \begin{tabular} \end{tabular} in LaTeX (one is math mode and the other is not). Is that right?

I have made progress with this problem, but I still have a few issues:
1) I have used the LayoutTable(() as suggested by Gavin
[@ LayoutTable(
[ [ $fig[0] , $fig[1] ],
[ $fig[2], $fig[3] ]] ) @]*
and it works fine. But I would like to add labels A,B,C,D to the graphics, as in my original version of the problem (that I understand now is not done right)
A [$fig[0]]* [: quad quad :] B [$fig[1]]*

C [$fig[2]]* [: quad quad :] D [$fig[3]]*

When I tried
[@ LayoutTable(
[ [ 'A' $fig[0] , 'B' $fig[1] ],
[ 'C' $fig[2],'D' $fig[3] ]] ) @]*
I got errors. On the other hand I do not want to include the label in the graphic because the correct answer is randomized by the problem, so the label will vary (as in my original version). What is the right way to add labels to the table entries with LayoutTable()in this situation?

2) If I use preformatted as suggested by Davide
: A [$fig[0]]* B[$fig[1]]*
: C[$fig[2]]* D[$fig[3]]*
I get each graphic on a new line (there is a linebreak even between A and B), so I do not get the layout I want. How can one prevent the linebreak here?

3) This is a more general issue, not specific to this problem: if I have a "vector"such as $f[$i] where $i=0,1,2,3, (this is probably not the right terminology, but I hope it's clear) , how do I use it in PGML? [$f[$i]] gives me errors, and so does [$f[[$i]]]. It seems to me that the square brackets [] have two different meanings.

Thanks again for any help.






In reply to Valerio De Angelis

Re: arrays in PGML

by Alex Jordan -
I'm not familiar with Array().

But LayoutTable() is a macro in niceTables.pl, to be distinguished from DataTable(). If your reason for making a "table" is purely to position things in a grid, and the x- and y-positions of the elements don't really matter, then using LayoutTable() will be better for accessibility reasons. In HTML output, instead of making an HTML table, it uses divs with special table-like styling. This is preferred by users with screen readers, because the HTML table structure is too rich. The screen readers would give the reader all the information about where each of your images are in the (nth row, mth column) form, and that's usually unnecessary and distracting.

Meanwhile in hard copy output, LayoutTable() and DataTable() both use tabular.

For more details on options for using LayoutTable(), see all the commentary on the top of the file here: https://github.com/openwebwork/pg/blob/master/macros/niceTables.pl

Try:

[@ LayoutTable(
[ [ 'A '.$fig[0] , 'B '.$fig[1] ],
[ 'C '.$fig[2], 'D '.$fig[3] ]] ) @]*

where I've added a space character and used the concatenation operator (period) to glue the image to the label. I'm not 100% sure this will give you what you want, as far as positioning goes. You might want to try things like:

[@ LayoutTable(
[ [ "A$PAR ".$fig[0] , "B$PAR ".$fig[1] ],
[ "C$PAR "..$fig[2], "D$PAR "..$fig[3] ]] ) @]*

Also, we have used a more elaborate method for similar questions to this that you might mimic: https://github.com/openwebwork/webwork-open-problem-library/blob/master/OpenProblemLibrary/PCC/BasicAlgebra/GraphingPointsAndLines/GraphLinearEquationByIntercepts10.pg


Is this your WeBWorK server?
http://webwork.xula.edu/webwork2/

If so, it looks like the version of WeBWorK is a bit old, maybe 2.7. Some issues you may be experiencing with PGML and array indices may (or may not) have been resolved in PGML by now.





In reply to Alex Jordan

Re: arrays in PGML

by Valerio De Angelis -
Yes, I only wanted to position the graphs in a grid. The LayoutTable() works well and the concatenation does exactly what I wanted to do with the labels, so this solved my problem with this issue. Thanks also for the links to the LayoutTable() documentation.

>Also, we have used a more elaborate method for similar questions to this that >you might mimic:
>https://github.com/openwebwork/webwork-open-problem-library/blob/master/OpenProblemLibrary/PCC/BasicAlgebra/GraphingPointsAndLines/GraphLinearEquationByIntercepts10.pg

this was useful too.

>Is this your WeBWorK server?
>http://webwork.xula.edu/webwork2/

No, that is actually an older server (from 2009) that we no longer use. Our currently working installation is from 2015 and it is here:

http://webwork2.xula.edu/webwork2

But we are currently in the process of updating again, and we are installing the latest version, that I hope will correct the Google Chrome bug (the vertical bar at the end of math content).





In reply to Valerio De Angelis

Re: arrays in PGML

by Davide Cervone -
The issue with the vertical bar is in MathJax, which was fixed in version 2.6 in December 2015. So if you update your copy of MathJax, that would take care of that. (You can update MathJax independent of the rest of WeBWorK.)
In reply to Valerio De Angelis

Re: arrays in PGML

by Davide Cervone -
Gavin has already indicated the problem with your code in (1), which is the missing concatenation operator: 'A '.$fig[0]. Note that you could also do "A $fig[0]" instead, if you wish.

For (2), it turns out that the image() macro includes a lot of extra spaces and some line breaks, so those are being included in the reformatted display.

One way around that is to remove them via regular expression substitution. Here is an example:

loadMacros("PGML.pl","PGgraphmacros.pl");

sub IMAGE {
  my $out = image(@_);
  $out =~ s/~~s*([~~n~~t]+~~s*)+|^~~s+|~~s+$//g;
  return $out;
}


$fig[0] = IMAGE("http://www.math.union.edu/random/pix/Folding-Cube.gif");
$fig[1] = IMAGE("http://www.math.union.edu/random/pix/helicoid.gif");
$fig[2] = IMAGE("http://www.math.union.edu/random/pix/Saddle-Slice.gif");
$fig[3] = IMAGE("http://www.math.union.edu/random/pix/TFB-2.gif");

BEGIN_PGML
:   A [$fig[0]]*  B [$fig[1]]*
:   
:   C [$fig[2]]*  D [$fig[3]]*
END_PGML
Here, I define a new IMAGE() macro to handle removing the unwanted spaces and newlines.

There are also several alternatives. You could use the imageRow() macro to generate a table with images and captions:

loadMacros("PGML.pl");

$fig[0] = "http://www.math.union.edu/random/pix/Folding-Cube.gif";
$fig[1] = "http://www.math.union.edu/random/pix/helicoid.gif";
$fig[2] = "http://www.math.union.edu/random/pix/Saddle-Slice.gif";
$fig[3] = "http://www.math.union.edu/random/pix/TFB-2.gif";

BEGIN_PGML
[@ imageRow([ $fig[0], $fig[1] ], ["A","B"]) @]*
[@ imageRow([ $fig[2], $fig[3] ], ["C","D"]) @]*
END_PGML
Finally, there are some extensions of the choice macros that make image selection lists in the Union macro library. These handle things like randomizing the order of questions and answers, but are more complicated to use.
loadMacros("PGML.pl","imageChoice.pl","contextABCD.pl","unionImage.pl");

$name = "http://www.math.union.edu/random/pix/Folding-Cube.gif";

$ml = new_image_match_list(
  link => 0,                #  don't link to separate image
  size => [100,100],            #  image size in pixels
  border => 0,              #  image already includes a border
);
$ml->{separation} = 3;      # separation for questions in the list

$ml->qa(               #  set the questions and answers
  "A Folding Cube", "http://www.math.union.edu/random/pix/Folding-Cube.gif",
  "A Helicoid", "http://www.math.union.edu/random/pix/helicoid.gif",
  "A Saddle Surface sliced by a plane", "http://www.math.union.edu/random/pix/Saddle-Slice.gif",
  "Projection of a torus from four-space", "http://www.math.union.edu/random/pix/TFB-2.gif",
);
$ml->choose(4);      #  select all of them

BEGIN_PGML
[@ $ml->print_q @]*

[@ $ml->print_a @]*
END_PGML

Context("ABCD");
ANS(string_cmp($ml->ra_correct_ans));

$showPartialCorrectAnswers = 0;
install_problem_grader(~~&std_problem_grader);

Finally, for (3), the issue with array section within PGML was fixed around two years ago, so you are probably using an old version of PGML.pl. If you update, that should allow you to use [$fig[$i]]. For now, however, you can use [$fig[ $i ]] (note the space between [ and $i).

In reply to Davide Cervone

Re: arrays in PGML

by Valerio De Angelis -
Thank you for the very detailed and helpful answer, I was able to solve all my problems with this. I would like to understand the definition of sub IMAGE, would you be able to point to something for me to read?

In reply to Valerio De Angelis

Re: arrays in PGML

by Davide Cervone -
There is not all that much to it, but there is one PG-specific wrinkle (that I will get to).

The first line uses the image() function and calls it with all the parameters passed to IMAGE(). For any subroutine, the @_ array is the array of arguments that it received, and image(@_) simply passes those on to image(). It stores the result in a local variable $out (the my means that variable is local to the IMAGE() subroutine).

The last line returns the value of $out so the only thing left to understand is the second line. That line performs a "regular expression" substitution. Regular expressions (or regex) are pattern matching expressions that are used to locate specific patterns within a string, and (in this case) replace them with other values. Many programming languages include them, so they have a fairly well-know syntax, but Perl regular expressions are documented here.

This is where the PG-specific item comes into play. Regular expressions have a means of indicating special characters (like newlines and tabs) or collections of characters (like "white space" in general), and this method uses backslashes to indicate them. But PG handles backslashes specially (because they are used in TeX code) and in such a way that they don't work properly in regular expressions within a PG file. So PG provides an alternative, which is ~~.

Every ~~ you see in the IMAGE() function should be a \ in normal Perl code. So the regular expression on the second line is actually

$out =~ s/\s*([\n\t]+\s*)+|^\s+|\s+$//g;
The =~ says apply the substitution on the right to the variable on the left (i.e., modify $out by the regex). The s/ indicates a substitution (as opposed to just a matching operation) is to be performed. The stuff up to the next (unescaped) / is the matching regular expression, and the stuff between that an the next / is what to replace it by. (So a substitution usually looks like s/regex/replacement/.

In this case, the regex is \s*([\n\t]+\s*)+|^\s+|\s+$, which I will explain in a moment, and the replacement string is empty (the material between the slashes in //). The trailing g means the substitution is global, meaning every substring that matches the regex will be replaced (not just the first one). So the upshot is that anything the matches the regex will be removes from the string (since the replacement string is empty).

So what matches this particular regex? This is where the reference I cite above comes in, but I'll explain this one now. The first thing to know is that | is used to separate sub-expressions, any one of which is allowed to be matched. So this means that there are three things that we are looking for, any one of which can be matched (i.e., all occurrences of these three patterns will be removed).

The three subexpressions are \s*([\n\t]+\s*)+, ^\s+,and \s+$. I will explain the last two first.

The caret (^) indicates the start of the string, so ^\s+ only will match something at the beginning of the string. Similarly, the dollar sign ($) matches the end of the string, so \s+$ only matches something at the end of the string. The \s indicates any white-space character (space, tab, non-breaking space, etc.), and + means that the preceding item must appear one or more times in a row. That is, ^\s+ matches all the spaces at the beginning of the string, while \s+$ matches all the spaces at the end of the string.

The regex \s*([\n\t]+\s*)+ starts with \s*. We already know \s matches while space; here the * means the preceding can match zero or more times. So this is zero or more spaces. The next part uses parentheses to group the regex [\n\t]+\s* so that + applies to that whole thing. That means one or more occurrences of stuff that matches [\n\t]+\s*. So this is [\n\t]+ followed by zero or more spaces.

The brackets are used to enclose a list of characters to match (e.g., [aeiou] would match one of the vowels), and in this case, we are matching \n and \t. These are the newline (\n) and tab (\t). So [\n\t]+ means one or more newlines or tabs, and [\n\t]+\s* means one or more newlines or tabs followed by optional spaces. Finally, ([\n\t]+\s*)+ means any number of those in a row (but at least one), and \s*([\n\t]+\s*)+ means optional spaces followed by newlines and tabs followed by optional spaces, repeated at least once.

Anything that matches any of those three patterns will be removed. So the upshot is that the second pattern removes any leading whitespace in the string, the third one removes any trailing whitespace in the string, and the first pattern removes any line breaks and the whitespace surrounding them.

So the upshot is to remove any line breaks and the whitespace around them, and any whitespace at the beginning or end of the string. This was the whitespace that was causing problems when the image was included in the pre-formatted output, since in pre-formatted output, multiple spaces and newlines are respected, while in regular formatted HTML they are collapsed to a single space.

Hope that helps.