Skip to content

Commit

Permalink
Update documentation to describe the new method of @problemset macro
Browse files Browse the repository at this point in the history
  • Loading branch information
kagalenko-m-b authored and kagalenko-m-b committed Jan 7, 2025
1 parent 0c50110 commit 41ff910
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 78 deletions.
10 changes: 5 additions & 5 deletions Generated_methods.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
```
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "ProblemSet"
uuid = "806f51fd-16a3-4f55-bd44-f0d72f9cf926"
authors = ["kagalenko-m-b <[email protected]> and contributors"]
version = "0.7.5"
version = "0.7.6"

[deps]
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
Expand Down
86 changes: 17 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
24 changes: 24 additions & 0 deletions Random_selection.md
Original file line number Diff line number Diff line change
@@ -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);
```
72 changes: 72 additions & 0 deletions Random_variations.md
Original file line number Diff line number Diff line change
@@ -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);
```
10 changes: 7 additions & 3 deletions src/ProblemSet.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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}
Expand All @@ -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(
Expand Down

2 comments on commit 41ff910

@kagalenko-m-b
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register

Release notes:

Added method to the @problemset macro for easy generation of a static set (no placeholder variables within the
problems' statements)

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/122541

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.7.6 -m "<description of version>" 41ff910a18502816378fe77ab8410510cb640c22
git push origin v0.7.6

Please sign in to comment.