Skip to content

Commit 734381e

Browse files
committed
"multiple answers" functionality added
The evaluation function will now handle multiple answers, either in the form of lists or using plus_minus and/or minus_plus. New parameters: multiple_answers_interpretation, with values "match_all", "match_any" and "match_order", and "plus_minus_split_preview", with values True and False. To be done: allow responses with missing list brackets, or (as far as possible) the wrong kind of brackets.
1 parent d965aba commit 734381e

2 files changed

Lines changed: 117 additions & 73 deletions

File tree

‎evaluate.m‎

Lines changed: 95 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,12 @@
118118
would parse as an assignment gets parsed instead as an equation, and also carries out
119119
other standard string replacements*)
120120

121-
StandardizeString[str_String]:=StringReplace[
122-
FixedPoint[StringReplace["==="->"=="],StringReplace[str,"="->"=="]],
123-
"**"->"^"]
121+
Options[StandardizeString] = {PlusMinusSplit->True};
124122

125-
StandardizeString[str_String]:=StringReplace[
123+
StandardizeString[str_String,OptionsPattern[]]:=Module[{output},output=StringReplace[
126124
FixedPoint[StringReplace["==="->"=="],StringReplace[str,"="->"=="]],
127-
"**"->"^"]
125+
{"**"->"^","plus_minus"->"\[PlusMinus]","minus_plus"->"\[MinusPlus]"}];
126+
If[OptionValue[PlusMinusSplit]&&StringContainsQ[output,{"\[PlusMinus]","\[MinusPlus]"}],output="{"<>StringReplace[output,{"\[PlusMinus]"->"+","\[MinusPlus]"->"-"}]<>", "<>StringReplace[output,{"\[PlusMinus]"->"-","\[MinusPlus]"->"+"}]<>"}"];output]
128127

129128
(*StandardizeExpression: a function that performs a number of standard replacements
130129
at the Expression stage, namely:
@@ -148,6 +147,16 @@
148147
output
149148
]
150149

150+
(* FullStandardizeString *)
151+
152+
Options[FullStandardizeString] = {SuppressIndependentVariable -> True};
153+
154+
FullStandardizeString[str_,OptionsPattern[]] := Module[{output,suppress},
155+
output=StandardizeString[str,PlusMinusSplit->True];
156+
output = ToExpression[output,TraditionalForm];
157+
output=StandardizeExpression[output,SuppressIndependentVariable->OptionValue[SuppressIndependentVariable]];
158+
ToString[output,InputForm]]
159+
151160
(*StructureMatchQ: a function that checks whether a user's response \
152161
has the same structure as a given answer template, given a set of \
153162
named variables.*)
@@ -187,21 +196,35 @@
187196

188197
Options[StructureMatchQ] = {Atomic -> False};
189198

190-
StructureMatchQ[answerTemplate_String,response_String,namedVariables_List] :=
199+
StructureMatchQ[answerTemplate_String,response_String,namedVariables_List,multipleAnswersInterpretation_String] :=
191200
Module[{response2,answerTemplate2},
192201
response2=MapAll[CanonicComplex,ReplaceAll[ToExpression[response],inertFunctionRules]];
193202
answerTemplate2=ReplaceAll[ToExpression[answerTemplate],inertFunctionRules];
194-
MatchQ[response2,Patternize[answerTemplate2,namedVariables]]]
203+
StructureMatchQ[answerTemplate2,response2,namedVariables,multipleAnswersInterpretation]]
204+
205+
StructureMatchQ[answerTemplate_,response_,namedVariables_List,multipleAnswersInterpretation_String] :=
206+
MatchQ[response,Patternize[answerTemplate,namedVariables]]
207+
208+
StructureMatchQ[answerTemplate_List,response_List,namedVariables_List,"match_all"]:=Apply[And,Apply[Or,Apply[StructureMatchQ[#1,#2,namedVariables,"match_all"]&,Outer[List,answerTemplate,response],{2}],{1}]]
209+
210+
StructureMatchQ[answerTemplate_List,response_,namedVariables_List,"match_all"]:=False
211+
212+
StructureMatchQ[answerTemplate_List,response_List,namedVariables_List,"match_any"]:=Apply[And,Apply[Or,Apply[StructureMatchQ[#1,#2,namedVariables,"match_all"]&,Outer[List,answerTemplate,response],{2}],{1}]]
213+
214+
StructureMatchQ[answerTemplate_List,response_,namedVariables_List,"match_any"]:=Apply[Or,Map[StructureMatchQ[#1,response,namedVariables,"match_any"]&,answerTemplate]]
215+
216+
StructureMatchQ[answerTemplate_List,response_List,namedVariables_List,"match_order"]:=Apply[And,Apply[Or,Apply[StructureMatchQ[#1,#2,namedVariables,"match_all"]&,Outer[List,answerTemplate,response],{2}],{1}]]
195217

196-
equalQStructure[answer_String, response_String, params_Association] := Module[{namedVariables,correctQ,expr,suppress},
218+
StructureMatchQ[answerTemplate_List,response_,namedVariables_List,"match_order"]:=Apply[And,Apply[StructureMatchQ[#1,#2,namedVariables,"match_order"]&,Transpose[{answerTemplate,response}],{1}]]
219+
220+
equalQStructure[answer_String, response_String, params_Association] := Module[{namedVariables,correctQ,standardizedAnswer,standardizedResponse,suppress,multipleAnswersInterpretation},
197221
Print["Evaluating Structure"];
198222
suppress = Lookup[params,"suppress_independent_variable",True];
199223
namedVariables = ToExpression[Lookup[params,"named_variables",{}],TraditionalForm];
200-
expr = StandardizeExpression[ToExpression[StandardizeString[answer],TraditionalForm],SuppressIndependentVariable->suppress];
201-
correctQ = StructureMatchQ[
202-
ToString[expr,InputForm],
203-
response,
204-
namedVariables];
224+
standardizedAnswer = FullStandardizeString[answer,SuppressIndependentVariable->suppress];
225+
standardizedResponse= FullStandardizeString[response,SuppressIndependentVariable->suppress];
226+
multipleAnswersInterpretation=Lookup[params,"multiple_answers_interpretation","match_all"];
227+
correctQ = StructureMatchQ[standardizedAnswer,standardizedResponse,namedVariables,multipleAnswersInterpretation];
205228

206229
<|
207230
"error" -> Null,
@@ -226,35 +249,48 @@
226249
exp -> Exp, log -> Log, ln -> Log, sqrt -> Sqrt,
227250
pi -> Pi, e -> E, i -> I};
228251

229-
SemanticMatchQ[answer_,response_] := TrueQ[Simplify[(response-answer)/.activeFunctionRules] == 0] ||
252+
SemanticMatchQ[answer_,response_,multipleAnswersInterpretation_String] := TrueQ[Simplify[(response-answer)/.activeFunctionRules] == 0] ||
230253
TrueQ[FullSimplify[(response-answer)/.activeFunctionRules] == 0]
231254

232-
SemanticMatchQ[answer_Equal, response_Equal] :=
233-
SemanticMatchQ[answer[[1]]-answer[[2]], response[[1]]-response[[2]]]||
234-
SemanticMatchQ[answer[[1]]-answer[[2]], response[[2]]-response[[1]]]||
235-
SemanticMatchQ[Denominator[Cancel[(answer[[1]]-answer[[2]])/(response[[1]]-response[[2]])]],1]||
236-
SemanticMatchQ[Denominator[Cancel[(response[[1]]-response[[2]])/(answer[[1]]-answer[[2]])]],1]
255+
SemanticMatchQ[answer_List,response_List,"match_all"]:=Apply[And,Apply[Or,Apply[SemanticMatchQ[#1,#2,"match_all"]&,Outer[List,answer,response],{2}],{1}]];
256+
257+
SemanticMatchQ[answer_List,response_,"match_all"]:=False;
258+
259+
SemanticMatchQ[answer_List,response_List,"match_any"]:=Apply[And,Apply[Or,Apply[SemanticMatchQ[#1,#2,"match_any"]&,Outer[List,answer,response],{2}],{1}]];
260+
261+
SemanticMatchQ[answer_List,response_,"match_any"]:=Apply[Or,Map[SemanticMatchQ[#1,response,"match_any"]&,answer]];
262+
263+
SemanticMatchQ[answer_List,response_List,"match_order"]:=Apply[And,Apply[SemanticMatchQ[#1,#2,"match_order"]&,Transpose[{answer,response}],{1}]];
237264

238-
SemanticMatchQ[answer_Equal, response_] := False
265+
SemanticMatchQ[answer_List,response_,"match_order"]:=False;
239266

240-
SemanticMatchQ[answer_, response_Equal] := False
267+
SemanticMatchQ[answer_Equal, response_Equal,multipleAnswersInterpretation_String] :=
268+
SemanticMatchQ[answer[[1]]-answer[[2]], response[[1]]-response[[2]],multipleAnswersInterpretation]||
269+
SemanticMatchQ[answer[[1]]-answer[[2]], response[[2]]-response[[1]],multipleAnswersInterpretation]||
270+
SemanticMatchQ[Denominator[Cancel[(answer[[1]]-answer[[2]])/(response[[1]]-response[[2]])]],1,multipleAnswersInterpretation]||
271+
SemanticMatchQ[Denominator[Cancel[(response[[1]]-response[[2]])/(answer[[1]]-answer[[2]])]],1,multipleAnswersInterpretation]
241272

242-
SemanticMatchQ[answer_String,response_String] :=
273+
SemanticMatchQ[answer_Equal, response_,multipleAnswersInterpretation_String] := False
274+
275+
SemanticMatchQ[answer_, response_Equal,multipleAnswersInterpretation_String] := False
276+
277+
SemanticMatchQ[answer_String,response_String,multipleAnswersInterpretation_String] :=
243278
SemanticMatchQ[
244279
ToExpression[answer],
245-
ToExpression[response]
280+
ToExpression[response],
281+
multipleAnswersInterpretation
246282
]
247283

248-
SemanticAndStructureMatchQ[answer_String,response_String,answerTemplate_String,namedVariables_List] :=
249-
TrueQ[SemanticMatchQ[answer,response]&&StructureMatchQ[answerTemplate,response,namedVariables]]
284+
SemanticAndStructureMatchQ[answer_String,response_String,answerTemplate_String,namedVariables_List,multipleAnswersInterpretation_String] :=
285+
TrueQ[SemanticMatchQ[answer,response,multipleAnswersInterpretation]&&StructureMatchQ[answerTemplate,response,namedVariables,multipleAnswersInterpretation]]
250286

251-
equalQSemantic[answer_String, response_String, params_Association] := Module[{correctQ, expr,suppress},
287+
equalQSemantic[answer_String, response_String, params_Association] := Module[{correctQ, standardizedAnswer,standardizedResponse,suppress,multipleAnswersInterpretation},
252288
Print["Evaluating Semantic"];
253289
suppress = Lookup[params,"suppress_independent_variable",True];
254-
expr = StandardizeExpression[ToExpression[StandardizeString[answer],TraditionalForm],SuppressIndependentVariable->suppress];
255-
correctQ = SemanticMatchQ[
256-
ToString[expr,InputForm],
257-
response];
290+
standardizedAnswer = FullStandardizeString[answer,SuppressIndependentVariable->suppress];
291+
standardizedResponse = FullStandardizeString[response,SuppressIndependentVariable->suppress];
292+
multipleAnswersInterpretation=Lookup[params,"multiple_answers_interpretation","match_all"];
293+
correctQ = SemanticMatchQ[standardizedAnswer,standardizedResponse,multipleAnswersInterpretation];
258294

259295
<|
260296
"error" -> Null,
@@ -263,18 +299,18 @@
263299
]
264300

265301
equalQSemanticAndStructure[answer_String, response_String, params_Association] := Module[{
266-
namedVariables,answerTemplate,correctQ,answerExpr,suppress},
302+
namedVariables,standardizedAnswer,standardizedResponse,answerTemplate,standardizedAnswerTemplate,correctQ,suppress,multipleAnswersInterpretation},
267303
Print["Evaluating SemanticAndStructure"];
268304
namedVariables = ToExpression[Lookup[params,"named_variables",{}],TraditionalForm];
269-
answerTemplate = Lookup[params,"answer_template",{}];
305+
answerTemplate = Lookup[params,"answer_template",Automatic];
270306
suppress = Lookup[params,"suppress_independent_variable",True];
271-
answerExpr = StandardizeExpression[ToExpression[StandardizeString[answer],TraditionalForm],SuppressIndependentVariable->suppress];
272-
correctQ = SemanticAndStructureMatchQ[
273-
ToString[answerExpr,InputForm],
274-
response,
275-
answerTemplate,
276-
namedVariables
277-
];
307+
standardizedAnswer= FullStandardizeString[answer,SuppressIndependentVariable->suppress];
308+
standardizedResponse= FullStandardizeString[response,SuppressIndependentVariable->suppress];
309+
multipleAnswersInterpretation=Lookup[params,"multiple_answers_interpretation","match_all"];
310+
standardizedAnswerTemplate=If[TrueQ[answerTemplate==Automatic],
311+
standardizedAnswer,
312+
FullStandardizeString[answerTemplate,SuppressIndependentVariable->suppress]];
313+
correctQ = SemanticAndStructureMatchQ[standardizedAnswer,standardizedResponse,standardizedAnswerTemplate,namedVariables,multipleAnswersInterpretation];
278314

279315
<|
280316
"error" -> Null,
@@ -295,27 +331,26 @@
295331

296332
(* IN PARTICULAR, THE COMPARISON OF THE LENGTHS OF THE SYMBOL LISTS IS VERY HAMFISTED *)
297333

298-
StrictStructureMatchQ[answerTemplate_String,response_String,namedVariables_List] :=
299-
StructureMatchQ[answerTemplate,response,namedVariables]&&
334+
StrictStructureMatchQ[answerTemplate_String,response_String,namedVariables_List,multipleAnswersInterpretation_String] :=
335+
StructureMatchQ[answerTemplate,response,namedVariables,multipleAnswersInterpretation]&&
300336
TrueQ[
301337
(Length[Union[UnnamedSymbols[ToExpression[response],namedVariables]]]==
302338
Length[Union[UnnamedSymbols[ToExpression[StandardizeString[answerTemplate],TraditionalForm],namedVariables]]])]
303339

304340
(* SemanticAndStrictStructureMatchQ: a function that combines a strict structure comparison with a test of
305341
mathematical equivalence *)
306342

307-
SemanticAndStrictStructureMatchQ[answer_String,response_String,answerTemplate_String,namedVariables_List] :=
308-
TrueQ[SemanticMatchQ[answer,response]&&StrictStructureMatchQ[answerTemplate,response,namedVariables]]
343+
SemanticAndStrictStructureMatchQ[answer_String,response_String,answerTemplate_String,namedVariables_List,multipleAnswersInterpretation_String] :=
344+
TrueQ[SemanticMatchQ[answer,response,multipleAnswersInterpretation]&&StrictStructureMatchQ[answerTemplate,response,namedVariables,multipleAnswersInterpretation]]
309345

310-
equalQStrictStructure[answer_String, response_String, params_Association] := Module[{namedVariables,correctQ,expr,suppress},
311-
Print["Evaluating Structure"];
346+
equalQStrictStructure[answer_String, response_String, params_Association] := Module[{namedVariables,correctQ,suppress,standardizedAnswer,standardizedResponse,multipleAnswersInterpretation},
347+
Print["Evaluating Strict Structure"];
312348
namedVariables = ToExpression[Lookup[params,"named_variables",{}],TraditionalForm];
313349
suppress = Lookup[params,"suppress_independent_variable",True];
314-
expr = StandardizeExpression[ToExpression[StandardizeString[answer],TraditionalForm],SuppressIndependentVariable->suppress];
315-
correctQ = StrictStructureMatchQ[
316-
ToString[expr,InputForm],
317-
response,
318-
namedVariables];
350+
standardizedAnswer = FullStandardizeString[answer,SuppressIndependentVariable->suppress];
351+
standardizedResponse= FullStandardizeString[response,SuppressIndependentVariable->suppress];
352+
multipleAnswersInterpretation=Lookup[params,"multiple_answers_interpretation","match_all"];
353+
correctQ = StrictStructureMatchQ[standardizedAnswer,standardizedResponse,namedVariables,multipleAnswersInterpretation];
319354

320355
<|
321356
"error" -> Null,
@@ -324,18 +359,18 @@
324359
]
325360

326361
equalQSemanticAndStrictStructure[answer_String, response_String, params_Association] := Module[{
327-
namedVariables,answerTemplate,correctQ,expr,suppress},
328-
Print["Evaluating SemanticAndStructure"];
362+
namedVariables,answerTemplate,correctQ,suppress,standardizedAnswer,standardizedResponse,standardizedAnswerTemplate,multipleAnswersInterpretation},
363+
Print["Evaluating SemanticAndStrictStructure"];
329364
namedVariables = ToExpression[Lookup[params,"named_variables",{}],TraditionalForm];
330-
answerTemplate = Lookup[params,"answer_template",{}];
365+
answerTemplate = Lookup[params,"answer_template",Automatic];
331366
suppress = Lookup[params,"suppress_independent_variable",True];
332-
expr = StandardizeExpression[ToExpression[StandardizeString[answer],TraditionalForm],SuppressIndependentVariable->suppress];
333-
correctQ = SemanticAndStrictStructureMatchQ[
334-
ToString[expr,InputForm],
335-
response,
336-
answerTemplate,
337-
namedVariables
338-
];
367+
standardizedAnswer= FullStandardizeString[answer,SuppressIndependentVariable->suppress];
368+
standardizedResponse= FullStandardizeString[response,SuppressIndependentVariable->suppress];
369+
standardizedAnswerTemplate=If[TrueQ[answerTemplate==Automatic],
370+
standardizedAnswer,
371+
FullStandardizeString[answerTemplate,SuppressIndependentVariable->suppress]];
372+
multipleAnswersInterpretation=Lookup[params,"multiple_answers_interpretation","match_all"];
373+
correctQ = SemanticAndStrictStructureMatchQ[standardizedAnswer,standardizedAnswer,standardizedAnswerTemplate,namedVariables,multipleAnswersInterpretation];
339374

340375
<|
341376
"error" -> Null,

‎preview.m‎

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818
(* Declare package context *)
1919
BeginPackage["preview`"];
2020

21-
PreviewFunction[response_, params_] := Module[{latexString, wolframString, parsedResponse, isLatex,suppress},Print["Running Preview Function"];
22-
Print["Preview Input:", response];
23-
24-
isLatex = Lookup[params,"is_latex",False];
25-
suppress=Lookup[params,"suppress_independent_variable",True];
26-
27-
parsedResponse = SafeToExpression[response, isLatex,suppress];
21+
PreviewFunction[response_, params_] := Module[{
22+
latexString, wolframString, parsedResponse, isLatex,suppress,plusMinusSplit},
23+
Print["Running Preview Function"];
24+
Print["Preview Input:", response];
25+
26+
isLatex = Lookup[params,"is_latex",False];
27+
suppress=Lookup[params,"suppress_independent_variable",True];
28+
plusMinusSplit = Lookup[params,"plus_minus_split_preview",True];
29+
30+
parsedResponse = SafeToExpression[response, isLatex,suppress,plusMinusSplit];
2831

2932
If[StringQ[parsedResponse] && StringStartsQ[parsedResponse, "Error:"],
3033
Return[
@@ -61,7 +64,7 @@
6164

6265
Begin["`Private`"];
6366

64-
SafeToExpression[str_String, isLatex_,suppress_] :=
67+
SafeToExpression[str_String, isLatex_,suppress_,plusMinusSplit_] :=
6568
Module[{expr, result},
6669
(* First check for obviously dangerous patterns in the raw string *)
6770
If[StringContainsQ[str,
@@ -73,11 +76,11 @@
7376

7477
If[isLatex,
7578
result = Quiet @ Check[
76-
ToExpression[StandardizeString[str], TeXForm, Hold],
79+
ToExpression[StandardizeString[str,PlusMinusSplit->plusMinusSplit], TeXForm, Hold],
7780
Return["Error: Failed to parse expression"]
7881
],
7982
result = Quiet @ Check[
80-
ToExpression[StandardizeString[str], TraditionalForm, Hold],
83+
ToExpression[StandardizeString[str,PlusMinusSplit->plusMinusSplit], TraditionalForm, Hold],
8184
Return["Error: Failed to parse expression"]
8285
]
8386
];
@@ -111,9 +114,15 @@
111114
of the equals sign in a string to the repeated equals sign, so that anything WL
112115
would parse as an assignment gets parsed instead as an equation*)
113116

114-
StandardizeString[str_String]:=StringReplace[
115-
FixedPoint[StringReplace["==="->"=="],StringReplace[str,"="->"=="]],
116-
"**"->"^"]
117+
Options[StandardizeString] = {PlusMinusSplit->True};
118+
119+
StandardizeString[str_String,OptionsPattern[]]:=Module[{output},
120+
output=StringReplace[
121+
FixedPoint[StringReplace["==="->"=="],StringReplace[str,"="->"=="]],
122+
{"**"->"^","plus_minus"->"\[PlusMinus]","minus_plus"->"\[MinusPlus]"}];
123+
If[OptionValue[PlusMinusSplit]&&StringContainsQ[output,{"\[PlusMinus]","\[MinusPlus]"}],
124+
output="{"<>StringReplace[output,{"\[PlusMinus]"->"+","\[MinusPlus]"->"-"}]<>", "<>StringReplace[output,{"\[PlusMinus]"->"-","\[MinusPlus]"->"+"}]<>"}"];
125+
output]
117126

118127
(*StandardizeExpression: a function that performs a number of standard replacements
119128
at the Expression stage, namely:

0 commit comments

Comments
 (0)