I am trying to typeset an exam and I would like to have questions and subquestions. I would like the total number of marks for the subquestions to be at the top of the question, but I don’t seem to able to use #context with time-travelling to achieve this goal.
I seem, at best, to be able to time travel to the second last subquestion, using <label2> in my code.
#let qnum = counter("questionnumber")
#let subqnum = counter("subquestionnumber")
#let subqmarks = counter("subquestionmarks")
#let question(content) = [
#qnum.step()
#subqnum.update(0) // any subquestions (a), (b), (c) start again...
#subqmarks.update(0)
*Question #qnum.display()*
(Total marks calculated as #context subqmarks.at(<label1>).first()? or #context subqmarks.at(<label2>).first()?)
#linebreak()
#content
<label1>
Actual total marks for this question: #subqmarks.display()
]
#let subquestion(nummarks: 0, content) = [
#subqnum.step()
#subqmarks.update(n => n + nummarks)
#block[#subqnum.display("a.") #content (#nummarks marks)]
]
#question[
#subquestion(nummarks: 3)[ 1 + 1 ]
#subquestion(nummarks: 4)[ 2 + 2 ]
#subquestion(nummarks: 5)[ 3 + 3 ]
#subquestion(nummarks: 6)[ 4 + 4 ]
<label2>
]
I do not understand why neither label1, nor label2, would tell me that the value of my subqmarks counter is 18 after all the subquestions have been considered.
Moreover, if there is a much better way to achieve my goal, then I’d also like to hear that because even if I understand this context stuff, I still have the problem that I need to generate and refer to a unique label for each question.
#content
<label1>
// or later
#subquestion(nummarks: 6)[ 4 + 4 ]
<label2>
the label is attached to the previous element (#content or the #subquestion). The associated location of these elements point to where they begin, which is before the counter is stepped.
What you want instead, is to have the label at an element after the content that contains the counter step. You can achieve this, for example, by attaching the label to an empty element:
#content
#[] <label1>
For your other problem of getting a unique label, you could use the current value of the #qnum counter, for example:
As an alternative approach, you can also use a separate marks counter for each question, and combine the question and sub-question counter into one. This eliminates the need for any labels:
Implementation
#let question-counter = counter("question")
#let marks-counter(num) = counter("marks-" + str(num))
#let question(content) = {
// Step main number, reset any sub-question number.
question-counter.update((num, ..) => num + 1)
context [
#let (num, ..) = question-counter.get()
#let total-marks = marks-counter(num).final().first()
*Question #num* (Total marks calculated as #total-marks)
#content
]
}
#let subquestion(nummarks: 0, content) = {
// Step only the sub-question number.
question-counter.step(level: 2)
context [
#let (num, sub) = question-counter.get()
// Update marks counter for this question number.
#marks-counter(num).update(n => n + nummarks)
#block[#numbering("(a)", sub) #content (#nummarks marks)]
]
}
Whether one approach is better than the other is hard to say, as whatever works is good enough
I prefer your alternative implementation but accepted your first answer since that addressed my immediate question, regarding being able to time travel. I hadn’t realised that labels were like functions which applied to the previous item, so that answer was helpful.
However, now that I have tried your alternative approach, I am faced with a new problem, namely, how to add up all the marks for the paper and get the total.
I added to the top:
#let overall-marks = counter("overall")
and then edited your definition of question, partly so I could have questions with a number of marks on their own and no subquestions:
#let question(nummarks: 0, content) = {
question-counter.update((num, ..) => num + 1)
context [
#let (num, ..) = question-counter.get()
#let total-marks = if nummarks == 0 { marks-counter(num).final().first() } else { nummarks }
#overall-marks.update(n => n + total-marks)
*Question #num* (#total-marks marks)
#content
The overall marks is now at #overall-marks.display()
]
}
However, this also seems to have the issue of always being a question behind in its totals (i.e., after the first question is printed, it says “The overall marks is now at 0.”)
Do you have any advice for how I can flesh out your alternative approach to keep track of the total marks too?
The problem here is that the counter update and counter display are within the same context block, in which the context represents the state before the overall-marks counter was updated. Thus, when the counter is displayed, it shows the value of the previous question.
To fix this, you can simply move the counter display in a separate context expression:
Thanks, this is excellent. I see that there is some discussion of #context within context in the Typst documentation, but I must admit I did not really understand it (as made evident by the questions above). I wonder if anyone will make a video explaining it really clearly for those of us who might have grown too lazy to read documentation thoroughly!