The approach you are taking has a number of problems. The main issue is that your checker does not handle handle the student answer correctly, and really can't. Your $answer
is a List object, but the ArbitraryString context can only produce a String object as a result, so the $student
variable will be a reference to an array of a single string. That means $n
will always be 1, and you will only ever check the first term of the correct answer, never the second or later ones.
- It appears you are trying to turn the student answer into a list, but that you are not doing this properly. First of all the substitution
s/~~)+/,/g
doesn't do what you think it does. It replaces one or more parentheses by a comma; it doesn't convert a parenthesis followed by a plus sign to a comma. That is because+
is a special character in regular expressions, so would have to be quoted to match against a literal plus sign. Also, you will lose the parentheses in this substitution, so perhaps you meants/~~)~~+/),/g
.
But even with that, the code won't work because
$stu
is now a string consisting of all the terms separated by commas, and you are checking one correct term against this string of all the student answers. You would need to split the string into its components at the commas and extract the one you need. (To do that, it would be best to split it once outside the loop so you don't go through the same process over and over again.)
But even if you did that, this doesn't do what you say you want to accomplish, which is to allow the student terms to be in any order, as you are checking the first correct with the first student part, and never any of the other student parts. Checking an unordered list is harder than that, in general.
Because your correct answer is a List, it will display as the terms separated by commas (not plus signs) if the student requests the correct answer. That will mean that the correct answer will be shown in a form that is not what the student should type. (This could be handled by changing the separator used for lists to a plus sign, but that is a hack).
Finally, if a student uses commas instead of plus signs, the answer would be marked as correct (if you followed my suggestions above, since you would first convert pluses to commas, then split at the commas, so if they had commas to start with, there would be no why to know that they didn't come from plusses).
So for all these reasons, I think you are going to have difficulty getting this approach to work.
My recommendation is to no use List MathObjects at all, but simply use the ArbitraryString context to use String objects as it is designed to do. Then use its custom checker to split the string on the plus signs (to get Perl arrays of the ions), sort those lists alphabetically (so they will be in the same order if correct), join them again by plus signs, and compare the results as single strings (rather than looking through them). This will not let you give partial credit (that could be done, but takes more work), but will make it possible to give credit for any order.
Here is the critical code that does the work:
$cation = $N_ion[0].$ion_at[0]."(aq)"; $anion = $N_ion[1].$ion_at[1]."(aq)"; $answer = Compute("$cation + $anion"); sub normalizeList { my $string = shift; $string =~ s/~~s//g; $string =~ s/(^|(?<=~~+))1(?=[A-Z])//g; my @list = lex_sort(split(/(?<![{^])~~+/, $string)); return join(' + ', @list); } ANS($answer->cmp( checker => sub { my ($correct,$student,$ans) = @_; return normalizeList($correct) eq normalizeList($student); } ));
This uses a function noramilzeList()
to convert the correct and student answers into a string that represents the sum of the terms in lexicographic order, and the checker simply compares these two lists as strings. We use $answer = compute(...)
to turn the original sum into the MathObject String. The normalizeList()
function includes code to remove a leading 1
from an element so that students don't have to type that (but allows it if they do).
While this does the job, it is still not a great approach, as it doesn't give the student any help with syntax errors, and doesn't allow you to provide partial credit. The best approach would be to develop a context that properly parses the chemical formulas that you are using. The contextReaction.pl
includes a context that does most of what you need. The only missing part is the powers and the (aq)
(I'm not a chemist, so I don't know what that means or what to call it). I don't think it would be too hard to add those to the reaction context, at least for someone familiar with MathObject contexts.