Wolfram Research Summer School 2016 Project.
WPL Skill Builder : An Infinite Coding Problems Generator in Wolfram Programming Language by Manjunath Babu
Abstract: By strengthening coding skills conceptually in Wolfram Programming Language, a wide variety of real world problems can be solved. Since the language is vast, a student takes significant amount of time in learning its crux of it in order to reap its benefits. Most people are not aware of many options/parameters available for a particular function and it takes time to discover the same. This project helps the programmer to build essential skills by exercising on randomly generated programming questions.
Background: Going through many practice problems is a great way to solidify your understanding of how the code should work. WPL Skill Builder is a program that generates short coding problem statements that gives immediate feedback on the platform or browser. To explore and solve big problems in computational universe, it requires fair amount of knowledge in using how various combinations of functions work. Someone who struggles in understanding the capabilities of important functions like NestList, Tuples, Thread, Cases etc. will find it harder to build towards their bigger picture.
Current Work:: At summer school, I intend to develop a working prototype by taking a small dataset of questions and randomizing it to a certain extent. I start by looking into the text book An Elementary Introduction to Wolfram Language by Stephen Wolfram. I converted some questions related to Strings Chapter into a JSON file and input to a variable in RAWJSON since it result in associations.
curatedSet = Import[FileNameJoin[{NotebookDirectory[], "curated.json"}], "RawJSON"]
In the curatedSet association, there are 4 fields. They are 1) question 2) answer 3) template 4) data
- Question: This variable holds the original question
- Answer: This variable holds the answer with template slots
- Template: This variable holds the question with template slots
- Data: This variable is a collections of randomizable data that will be filled into the template slots.
With the above association structure, various questions can be generated with certain degree of randomness. Here is one such example:
- Question: Make a PieChart of values {1,5,6,3}
- Answer: PieChart[{1,2,6,3}]
- Template: Make a
FunctionTemplateSlot
of values RandomValues
- Data: {"FunctionTemplateSlot":["PieChart","BarChart,","ListLinePlot"],"RandomValues":"RandomInteger[{1,10},4]"]
In the above example, every time a new question generates, the question and answer will be filled with the actual function name. I found these types of questions to be very easy by the users to write the answers. In order to tackle this problem, we came up by introducing a logic map. Here is how it works.
A Logic Map basically maps a certain portion of the question, a natural language into a Wolfram Function.
logicMap[s_] := Replace[s, {
any_List :> StringJoin@Riffle[Map[logicMap, any], ", "],
"PieChart3D" -> "3D Pie Chart",
"PieChart" -> "Pie Chart",
"BarChart" -> "Bar Chart",
"BarChart3D" -> "3D Bar Chart",
"ListLinePlot" -> "List Line Plot",
"ToUpperCase" -> "in Upper Case Letters",
"ToLowerCase" -> "in Lower Case Letters",
"StringReverse" -> "in Reverse Order",
"StringIdentity" -> "in Ascending Order", (*StringIdentity is a not a function but \
internally, It uses an Identity Function*)
"Greater" -> "in descending order",
any_ :> TextString[any]
}]
By introducing a logic map, I was able to generate questions more like a natural language. It felt less computer generated.
At this point, everything worked fine. However, I faced another issue. I was unable to randomize questions with unequal number of functions. Here is an example:
- Question 1: Join 3 copies of string "
hello
". Answer 1: StringJoin[Table["hello",3"]]
- Question 2: Join 3 copies of string "
hello
" in reverse order. Answer 2: StringReverse[StringJoin[Table[]]]
In the above statements, Answer 2 has an extra function. To templatize this, it takes additional mapping to do. You can call it a placeholder. One function we could think of was Identity
function. This function basically returns whatever was inside it. Now we write the function map:
userFuncNameReplace[s_] := Replace[s, {
"StringIdentity" -> "Identity",
any_ :> TextString[any]
}]
And its corresponding JSON File contents is as given below:
- Question: Join 3 copies of string "
hello
"
- Answer:
order
[StringJoin[StringJoin[Table[\"String
\", Number
]]]]"
- Template: Join
RandomNumber
copies of the string \"RandomString
\" order
.
- Data: {"
RandomNumber
":"RandomInteger[{4,10}]","RandomString
":"RandomChoice[ Select[WordList[], StringLength[#] == 6 && DictionaryWordQ[#] &] ]","order
":["StringReverse","StringIdentity"]}
CAUTION:
In the above example, I am using the DictionaryWordQ
and Wordlist
function to generate random words. When playing with word data, there may be a lot of bad words and we need to filter those. We can use Machine Learning Function called Classify
and Profanity
as its parameter to filter out all the bad words. Here is the code to generate them.
Select[DictionaryLookup[], Classify["Profanity", #] &]
We need to use the above code to filter out profane words while generating random words for our application.
Once this code is in place, for a randomly generated question, there can be 2 possibilities for the function order
. Either ascending order or descending order. For ascending order, there is no need of an additional function. However for descending order, we need StringReverse
function.
In this case, if the question generated is ascending order, then, the data pulled from JSON will be a function called StringIdentity
. If you notice, in Wolfram Language, there is no function called StringIdentity. But, since we are using the function mapping, it clearly maps to the Identity
function for the answer template slot. And for the question template slot, it will pull the natural language context from the logic map that turns out to be "in ascending order". Hence, by function mapping and logic mapping, a completely random question can be generated flawlessly.
Applying Templates With curated dataset in rawJSON, Logic Maps, Function Maps in place, we now have to define a template apply function which substitutes it all the available template slots. The below mentioned code is a user defined function called templateapply
.
templateApplyFunction[q_Association] := TemplateApply[
{StringTemplate[q["Template"], InsertionFunction -> logicMap],
StringTemplate[q["Answer"],
InsertionFunction -> userFuncNameReplace]},
Map[Replace[#, {s_String :> ToExpression[s],
l_List :> RandomChoice[l]}] &, q["Data"]]
]
templateApplyFunction
will take the templatized pair of questions and answers, fills the slots carefully with data from the JSON file after logically mapping the natural language and logically selecting the appropriate function. Since data variable in the JSON is a list, our templateApplyFunction
has a RandomChoice function that will pick a particular value from many.
Generate Random Problem: With all the edge cases handled by writing the above mapping functions, now comes the time to generate a random question. Below 2 lines of code will serve the purpose. The 1st line will generate a specific question for testing purpose. The 2nd line will generate a completely random question. Question are generated from the curatedSet variable, the input from the JSON.
generateRandomProblem[n_] := templateApplyFunction@curatedSet[[n]]
generateRandomProblem[] := templateApplyFunction@RandomChoice[curatedSet]
Immediate Output on Notebook: To view an immediate output on the notebook, the following code will have to be used:
{question, solution} = generateRandomProblem[17];
question
solution
ToExpression[solution]
Sample Output:
Deploy: Using form function, cloud deploy, tool tip, URLBuild etc.. functions, we can deploy this application in the cloud and make it available to users. Click this URL to try it now: https://wolfr.am/e0t5Zn50
Follow Up: I'm very Interested in building a fully functional web application with many features. At summer school, I developed a working prototype by taking a small dataset of questions. The future of this project would be to enhance the complexity by using leaf count
of the solution, tree form
, implementing an M-ary tree and alternative approaches for exercising various options parameters in a given solution. Its extension is to build a personalized website that displays user profile on the dashboard screen with various areas of WPL listed like Strings, Graphics, Anonymous Functions, Datasets, Machine Learning, Sound, Real World Data etc.. Based on a pre-assessment test, user gets the performance results. With all weak areas listed, user can pick a specific area like Patterns, Cases, Select etc.. and continue practicing all kinds of randomly generated questions to strengthen the programming skills. The performance results of entire user-base can further be used by the company for detailed training analysis and research.
Leave your feedback on the comments section.
Thank you.