From 41ff910a18502816378fe77ab8410510cb640c22 Mon Sep 17 00:00:00 2001 From: kagalenko-m-b Date: Tue, 7 Jan 2025 19:23:34 +0300 Subject: [PATCH] Update documentation to describe the new method of @problemset macro --- Generated_methods.md | 10 +++--- Project.toml | 2 +- README.md | 86 +++++++++----------------------------------- Random_selection.md | 24 +++++++++++++ Random_variations.md | 72 +++++++++++++++++++++++++++++++++++++ src/ProblemSet.jl | 10 ++++-- 6 files changed, 126 insertions(+), 78 deletions(-) create mode 100644 Random_selection.md create mode 100644 Random_variations.md diff --git a/Generated_methods.md b/Generated_methods.md index 5f4ad06..2005fae 100644 --- a/Generated_methods.md +++ b/Generated_methods.md @@ -1,7 +1,7 @@ ## Methods of the generated functions Each `@problem` macro generates in the workspace four methods to be used for -producing the individual problems. Let's take an example: +producing the individual problems. Consider an example: ```julia @problem sub_add begin z ~ rand(7:9) @@ -14,7 +14,7 @@ producing the individual problems. Let's take an example: of two values: \(a = %z%\) and \(b = %w%\) """ @text_solution raw""" - Difference is equal to \(c = %zw_sub%\), sum is equal to \(c = %zw_add%\) + Difference is equal to \(c = %zw_sub%\), sum is equal to \(d = %zw_add%\) """ end ``` @@ -48,13 +48,13 @@ sub_add(::Val{:solution_text}) = TokenText( ) ``` -When processing the macros, the left-hand sides of tilde `~` operators are collected -and then the tildes are replaced by the equality signs `=`. Otherwise the syntax +Macro collects the left-hand sides of tilde `~` operators +and replaces the tildes with the equality signs `=`. Otherwise the syntax within the macro is the usual Julian syntax. The left-hand side of a tilde must always be a single variable; assignment to a tuple is unsupported. The value assigned to that variable may be a matrix or a tuple. Indexing of text variables is supported. -The first method generates (usually randomized) data for the problem's statement. +The first method generates data for the problem's statement. The second method computes the solution. Arguments for this method are the symbols that appear at the left-hand side of the tilde operator within the `@problem` macro, diff --git a/Project.toml b/Project.toml index 5186712..73fa1a6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ProblemSet" uuid = "806f51fd-16a3-4f55-bd44-f0d72f9cf926" authors = ["kagalenko-m-b <16374215+kagalenko-m-b@users.noreply.github.com> and contributors"] -version = "0.7.5" +version = "0.7.6" [deps] MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" diff --git a/README.md b/README.md index 06d9329..cdbaae0 100644 --- a/README.md +++ b/README.md @@ -2,73 +2,21 @@ [![Build Status](https://github.com/kagalenko-m-b/ProblemSet.jl/workflows/CI/badge.svg)](https://github.com/kagalenko-m-b/ProblemSet.jl/actions) -The goal of this project is to facilitate the creation of problem assignments -for a group of students. A problem set consists of several problem templates with -text and placeholder variables. Percentage signs before and after mark the -placeholder variables within the template text. Function `problemset_latex()` -then generates the latex source of the assignment and solutions for a vector of students' -names. In the latex source assigned values replace the placeholder variables. -Every placeholder variable must appear at least once as the left-hand of -an assignment where the tilde replaces the equality sign. +The goal of this package is to facilitate the creation of problem assignments +for a group of students. It allows to randomize the assignments in two ways: -This is an example set: -```julia -@problemset my_set begin -@problem pool begin - pool_size_liters ~ rand(1000:10:2000) - inflow_liters_sec ~ rand(10:20) - outflow_max = inflow_liters_sec ÷ 2 - outflow_liters_sec ~ rand(1:outflow_max) - @solution begin - fill_rate = inflow_liters_sec - outflow_liters_sec - time_to_fill = pool_size_liters / fill_rate - time_to_fill_min ~ round(time_to_fill / 60, digits=3) - leaked_liters ~ round(time_to_fill*outflow_liters_sec, digits=3) - end - @text """ - An empty pool can hold %pool_size_liters% liters of water. Pipe - fills it at the rate %inflow_liters_sec%~liters/sec while another - drains it at the rate %outflow_liters_sec%~liters/sec. How many minutes - will it take to fill the pool and how many liters of water will - drain out by the time the pool is full? - """ - @text_solution """ - It will take %time_to_fill_min% minutes to fill the pool and - %leaked_liters%~liters of water will drain out. - """ -end -@problem addition begin - x ~ rand(1:3) - y ~ rand(2:5) - @solution xy ~ x + y - @text raw""" Find the sum \(c = a + b\) of two values: \(a = %x%\) and - \(b = %y%\) - """ - @text_solution raw""" - Sum is equal to \(c = %xy%\) - """ -end -@problem subtraction begin - z ~ rand(7:9) - w ~ rand(1:5) - @solution zw ~ z - w - @text raw""" Find the difference \(c = a - b\) of two values: \(a = %z%\) and - \(b = %w%\) - """ - @text_solution raw""" - Difference is equal to \(c = %zw%\) - """ -end -end -``` -After execution of this macro, there's a vector in workspace named `my_set` -that contains three functions `my_set_pool()`, `my_set_addition()` and -`my_set_subtraction()`. Text-generating function makes use of their -[methods](Generated_methods.md). This is how an assignment may be produced: -```julia -student_names = ["A", "B", "C"]; -rng_seed = 123; -txt,txt_sol = problemset_latex(student_names, my_set, 2=>1:3, rng_seed); -write("problems.tex", latex_preamble*txt); -write("solutions.tex", latex_preamble*txt); -``` +* [random selection](Random_selection.md) of problems from a set, and + +* [random variations](Random_variations.md) of the specified parts of an individual +problem's statement. + +Those two kinds of randomization may be combined. + +A problem set consists of several problem templates. Each template may hold the textual +statement of the problem, its solution and fragments of Julia code to vary +the values of placeholder variables within the statement and the solution. + +The package exports macros `@problem` and `@problemset` that create a template and a set, +respectively. Function `problemset_latex()` generates the latex sources of +assignments and their solutions. To ensure reproducibility, it calls `Random.seed!()` +with an incremental value before generating each succesive problem. diff --git a/Random_selection.md b/Random_selection.md new file mode 100644 index 0000000..c888138 --- /dev/null +++ b/Random_selection.md @@ -0,0 +1,24 @@ +## Random selection of problems from a set + +A simplest set may be created by giving to the `@problemset` macro a list of questions +as a multiline string: +```julia +@problemset question_set """The first question. +The second question. +... +The tenth question." +``` + +This creates a workspace vector named `question_set` that contains ten functions +`question_set_1()`, `question_set_2()`, … `question_set_10()`. Suppose you wish +an individual assignment to contain one question from the first half of the vector +and two questions from the second half. That may be accomplished as follows: +```julia +student_names = ["A", "B", "C", "D"]; +rng_seed = 123; +subsets = [1=>1:5, 2=>6:10] +txt, = problemset_latex(student_names, question_set, subsets, rng_seed); +write("questions.tex", latex_preamble()*txt); +``` + + diff --git a/Random_variations.md b/Random_variations.md new file mode 100644 index 0000000..1dc10a6 --- /dev/null +++ b/Random_variations.md @@ -0,0 +1,72 @@ +## Random variation of a problem's statement + +Problem template will typically include the statement's text with a few placeholder +variables. Percentage signs before and after mark the +placeholder variables within the template text. Function `problemset_latex()` +then generates the latex source of the assignment and solutions for a vector of students' +names. In the latex source assigned values replace the placeholder variables. +Every placeholder variable must appear at least once as the left-hand of +an assignment where the tilde replaces the equality sign. + +This is an example set: +```julia +@problemset my_set begin +@problem pool begin + pool_size_liters ~ rand(1000:10:2000) + inflow_liters_sec ~ rand(10:20) + outflow_max = inflow_liters_sec ÷ 2 + outflow_liters_sec ~ rand(1:outflow_max) + @solution begin + fill_rate = inflow_liters_sec - outflow_liters_sec + time_to_fill = pool_size_liters / fill_rate + time_to_fill_min ~ round(time_to_fill / 60, digits=3) + leaked_liters ~ round(time_to_fill*outflow_liters_sec, digits=3) + end + @text """ + An empty pool can hold %pool_size_liters% liters of water. Pipe + fills it at the rate %inflow_liters_sec%~liters/sec while another + drains it at the rate %outflow_liters_sec%~liters/sec. How many minutes + will it take to fill the pool and how many liters of water will + drain out by the time the pool is full? + """ + @text_solution """ + It will take %time_to_fill_min% minutes to fill the pool and + %leaked_liters%~liters of water will drain out. + """ +end +@problem addition begin + x ~ rand(1:3) + y ~ rand(2:5) + @solution xy ~ x + y + @text raw""" Find the sum \(c = a + b\) of two values: \(a = %x%\) and + \(b = %y%\) + """ + @text_solution raw""" + Sum is equal to \(c = %xy%\) + """ +end +@problem subtraction begin + z ~ rand(7:9) + w ~ rand(1:5) + @solution zw ~ z - w + @text raw""" Find the difference \(c = a - b\) of two values: \(a = %z%\) and + \(b = %w%\) + """ + @text_solution raw""" + Difference is equal to \(c = %zw%\) + """ +end +end +``` + +Executing this macro creates a workspace vector named `my_set` +that contains three functions `my_set_pool()`, `my_set_addition()` and +`my_set_subtraction()`. Text-generating function makes use of their +[methods](Generated_methods.md). This is how an assignment may be produced: +```julia +student_names = ["A", "B", "C", "D"]; +rng_seed = 123; +txt,txt_sol = problemset_latex(student_names, my_set, 2=>1:3, rng_seed); +write("problems.tex", latex_preamble()*txt); +write("solutions.tex", latex_preamble()*txt); +``` diff --git a/src/ProblemSet.jl b/src/ProblemSet.jl index 58da908..2f98a32 100644 --- a/src/ProblemSet.jl +++ b/src/ProblemSet.jl @@ -3,7 +3,7 @@ module ProblemSet using MacroTools using Random -export @problem, @problemset, TokenText, problemset_latex, latex_preamble +export TokenText, @problem, @problemset, problemset_latex, latex_preamble struct TokenText strings::Vector{<:AbstractString} @@ -15,8 +15,8 @@ struct TokenText end include("problem_compiler.jl") -latex_preamble = """ -\\documentclass[a4paper,12pt,notitlepage]{article} +latex_preamble(;font_size_pt::Integer=12, default_language=:english) = """ +\\documentclass[a4paper,$(font_size_pt)pt,notitlepage]{article} \\usepackage{amsmath} \\usepackage[left=1.cm,right=1cm,top=1cm,bottom=1cm]{geometry} \\pagenumbering{gobble} @@ -26,7 +26,11 @@ latex_preamble = """ \\usepackage{float} \\usepackage{graphicx} \\usepackage{bookmark} +\\usepackage{tabularx} +\\usepackage[table]{xcolor} +\\setdefaultlanguage{$default_language} \\setmainfont{Liberation Serif} +\\setmonofont{Liberation Mono} \\setsansfont{Liberation Sans}\n\n""" function select_problems(