Skip to content

Commit d965aba

Browse files
committed
Parsing of derivatives added
Derivatives of the form dy/dx are converted to y'[x]. With the parameter "suppress_independent_variable" set to True (the default), y'[x] then becomes simply y'. This happens for both answers and responses, meaning both evaluate.m and preview.m are affected.
1 parent fc094c1 commit d965aba

2 files changed

Lines changed: 71 additions & 18 deletions

File tree

evaluate.m

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,32 @@
122122
FixedPoint[StringReplace["==="->"=="],StringReplace[str,"="->"=="]],
123123
"**"->"^"]
124124

125+
StandardizeString[str_String]:=StringReplace[
126+
FixedPoint[StringReplace["==="->"=="],StringReplace[str,"="->"=="]],
127+
"**"->"^"]
128+
129+
(*StandardizeExpression: a function that performs a number of standard replacements
130+
at the Expression stage, namely:
131+
- replacing s_[arg_Plus] by s*(arg) unless s is a symbol representing a known function
132+
- replacing expressions of the form dy_^n_/dx_^n_ with y'[x]^n
133+
- if the option SuppressIndependentVariable is set to True, replacing each y'[x] with y'*)
134+
135+
Options[StandardizeExpression] = {SuppressIndependentVariable -> True};
136+
137+
StandardizeExpression[expr_, OptionsPattern[]]:=Module[{output,suppress},
138+
suppress = OptionValue[SuppressIndependentVariable];
139+
output = expr/.s_Symbol[arg_Plus]/;Not[MemberQ[Attributes[s], NumericFunction]] :> s*arg;
140+
output = output/.{
141+
dx_^a_. dy_^b_.:>
142+
(ToExpression[StringTake[ToString[dx],{2}]]'[StringTake[ToString[dy],{2}]])^a/;
143+
StringTake[ToString[dx],{1}]=="d"&&StringTake[ToString[dy],{1}]=="d"&&a>0&&a+b==0,
144+
dx_^a_. dy_^b_.:>
145+
(ToExpression[StringTake[ToString[dy],{2}]]'[StringTake[ToString[dx],{2}]])^a/;
146+
StringTake[ToString[dx],{1}]=="d"&&StringTake[ToString[dy],{1}]=="d"&&b>0&&a+b==0};
147+
If[suppress,output=output/.Derivative[n_][y_][x_]:>Derivative[n][y]];
148+
output
149+
]
150+
125151
(*StructureMatchQ: a function that checks whether a user's response \
126152
has the same structure as a given answer template, given a set of \
127153
named variables.*)
@@ -167,10 +193,11 @@
167193
answerTemplate2=ReplaceAll[ToExpression[answerTemplate],inertFunctionRules];
168194
MatchQ[response2,Patternize[answerTemplate2,namedVariables]]]
169195

170-
equalQStructure[answer_String, response_String, params_Association] := Module[{namedVariables,correctQ,expr},
196+
equalQStructure[answer_String, response_String, params_Association] := Module[{namedVariables,correctQ,expr,suppress},
171197
Print["Evaluating Structure"];
198+
suppress = Lookup[params,"suppress_independent_variable",True];
172199
namedVariables = ToExpression[Lookup[params,"named_variables",{}],TraditionalForm];
173-
expr = ToExpression[StandardizeString[answer],TraditionalForm] /.s_Symbol[arg_Plus]/;Not[MemberQ[Attributes[s], NumericFunction]] :> s*arg;
200+
expr = StandardizeExpression[ToExpression[StandardizeString[answer],TraditionalForm],SuppressIndependentVariable->suppress];
174201
correctQ = StructureMatchQ[
175202
ToString[expr,InputForm],
176203
response,
@@ -221,9 +248,10 @@
221248
SemanticAndStructureMatchQ[answer_String,response_String,answerTemplate_String,namedVariables_List] :=
222249
TrueQ[SemanticMatchQ[answer,response]&&StructureMatchQ[answerTemplate,response,namedVariables]]
223250

224-
equalQSemantic[answer_String, response_String, params_Association] := Module[{correctQ, expr},
251+
equalQSemantic[answer_String, response_String, params_Association] := Module[{correctQ, expr,suppress},
225252
Print["Evaluating Semantic"];
226-
expr = ToExpression[StandardizeString[answer],TraditionalForm]/.s_Symbol[arg_Plus]/;Not[MemberQ[Attributes[s], NumericFunction]] :> s*arg;
253+
suppress = Lookup[params,"suppress_independent_variable",True];
254+
expr = StandardizeExpression[ToExpression[StandardizeString[answer],TraditionalForm],SuppressIndependentVariable->suppress];
227255
correctQ = SemanticMatchQ[
228256
ToString[expr,InputForm],
229257
response];
@@ -235,11 +263,12 @@
235263
]
236264

237265
equalQSemanticAndStructure[answer_String, response_String, params_Association] := Module[{
238-
namedVariables,answerTemplate,correctQ,answerExpr},
266+
namedVariables,answerTemplate,correctQ,answerExpr,suppress},
239267
Print["Evaluating SemanticAndStructure"];
240268
namedVariables = ToExpression[Lookup[params,"named_variables",{}],TraditionalForm];
241-
answerTemplate = Lookup[params,"answer_template",{}];
242-
answerExpr = ToExpression[StandardizeString[answer],TraditionalForm]/.s_Symbol[arg_Plus]/;Not[MemberQ[Attributes[s], NumericFunction]] :> s*arg;;
269+
answerTemplate = Lookup[params,"answer_template",{}];
270+
suppress = Lookup[params,"suppress_independent_variable",True];
271+
answerExpr = StandardizeExpression[ToExpression[StandardizeString[answer],TraditionalForm],SuppressIndependentVariable->suppress];
243272
correctQ = SemanticAndStructureMatchQ[
244273
ToString[answerExpr,InputForm],
245274
response,
@@ -278,10 +307,11 @@
278307
SemanticAndStrictStructureMatchQ[answer_String,response_String,answerTemplate_String,namedVariables_List] :=
279308
TrueQ[SemanticMatchQ[answer,response]&&StrictStructureMatchQ[answerTemplate,response,namedVariables]]
280309

281-
equalQStrictStructure[answer_String, response_String, params_Association] := Module[{namedVariables,correctQ,expr},
310+
equalQStrictStructure[answer_String, response_String, params_Association] := Module[{namedVariables,correctQ,expr,suppress},
282311
Print["Evaluating Structure"];
283312
namedVariables = ToExpression[Lookup[params,"named_variables",{}],TraditionalForm];
284-
expr = ToExpression[StandardizeString[answer],TraditionalForm]/.s_Symbol[arg_Plus]/;Not[MemberQ[Attributes[s], NumericFunction]] :> s*arg;
313+
suppress = Lookup[params,"suppress_independent_variable",True];
314+
expr = StandardizeExpression[ToExpression[StandardizeString[answer],TraditionalForm],SuppressIndependentVariable->suppress];
285315
correctQ = StrictStructureMatchQ[
286316
ToString[expr,InputForm],
287317
response,
@@ -294,11 +324,12 @@
294324
]
295325

296326
equalQSemanticAndStrictStructure[answer_String, response_String, params_Association] := Module[{
297-
namedVariables,answerTemplate,correctQ,expr},
327+
namedVariables,answerTemplate,correctQ,expr,suppress},
298328
Print["Evaluating SemanticAndStructure"];
299329
namedVariables = ToExpression[Lookup[params,"named_variables",{}],TraditionalForm];
300330
answerTemplate = Lookup[params,"answer_template",{}];
301-
expr = ToExpression[StandardizeString[answer],TraditionalForm]/.s_Symbol[arg_Plus]/;Not[MemberQ[Attributes[s], NumericFunction]] :> s*arg;
331+
suppress = Lookup[params,"suppress_independent_variable",True];
332+
expr = StandardizeExpression[ToExpression[StandardizeString[answer],TraditionalForm],SuppressIndependentVariable->suppress];
302333
correctQ = SemanticAndStrictStructureMatchQ[
303334
ToString[expr,InputForm],
304335
response,

preview.m

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

21-
PreviewFunction[response_, params_] := Module[{latexString, wolframString, parsedResponse, isLatex},
22-
Print["Running Preview Function"];
23-
Print["Preview Input:", response];
21+
PreviewFunction[response_, params_] := Module[{latexString, wolframString, parsedResponse, isLatex,suppress},Print["Running Preview Function"];
22+
Print["Preview Input:", response];
2423

25-
isLatex = Lookup[params,"is_latex",False];
24+
isLatex = Lookup[params,"is_latex",False];
25+
suppress=Lookup[params,"suppress_independent_variable",True];
2626

27-
parsedResponse = SafeToExpression[response, isLatex];
27+
parsedResponse = SafeToExpression[response, isLatex,suppress];
2828

2929
If[StringQ[parsedResponse] && StringStartsQ[parsedResponse, "Error:"],
3030
Return[
@@ -61,7 +61,7 @@
6161

6262
Begin["`Private`"];
6363

64-
SafeToExpression[str_String, isLatex_] :=
64+
SafeToExpression[str_String, isLatex_,suppress_] :=
6565
Module[{expr, result},
6666
(* First check for obviously dangerous patterns in the raw string *)
6767
If[StringContainsQ[str,
@@ -101,7 +101,7 @@
101101
Get, Put, Install, Uninstall
102102
]],
103103
"Error: Expression contains unsafe constructs",
104-
expr /. s_Symbol[arg_Plus]/;Not[MemberQ[Attributes[s], NumericFunction]] :> s*arg (* safe expression *)
104+
StandardizeExpression[expr,SuppressIndependentVariable->suppress](* safe expression *)
105105
],
106106
"Error: Unexpected parsing result"
107107
]
@@ -115,6 +115,28 @@
115115
FixedPoint[StringReplace["==="->"=="],StringReplace[str,"="->"=="]],
116116
"**"->"^"]
117117

118+
(*StandardizeExpression: a function that performs a number of standard replacements
119+
at the Expression stage, namely:
120+
- replacing s_[arg_Plus] by s*(arg) unless s is a symbol representing a known function
121+
- replacing expressions of the form dy_^n_/dx_^n_ with y'[x]^n
122+
- if the option SuppressIndependentVariable is set to True, replacing each y'[x] with y'*)
123+
124+
Options[StandardizeExpression] = {SuppressIndependentVariable -> True};
125+
126+
StandardizeExpression[expr_, OptionsPattern[]]:=Module[{output,suppress},
127+
suppress = OptionValue[SuppressIndependentVariable];
128+
output = expr/.s_Symbol[arg_Plus]/;Not[MemberQ[Attributes[s], NumericFunction]] :> s*arg;
129+
output = output/.{
130+
dx_^a_. dy_^b_.:>
131+
(ToExpression[StringTake[ToString[dx],{2}]]'[StringTake[ToString[dy],{2}]])^a/;
132+
StringTake[ToString[dx],{1}]=="d"&&StringTake[ToString[dy],{1}]=="d"&&a>0&&a+b==0,
133+
dx_^a_. dy_^b_.:>
134+
(ToExpression[StringTake[ToString[dy],{2}]]'[StringTake[ToString[dx],{2}]])^a/;
135+
StringTake[ToString[dx],{1}]=="d"&&StringTake[ToString[dy],{1}]=="d"&&b>0&&a+b==0};
136+
If[suppress,output=output/.Derivative[n_][y_][x_]:>Derivative[n][y]];
137+
output
138+
]
139+
118140

119141

120142
End[];

0 commit comments

Comments
 (0)