求助:使用exams2nops时,同习题多次调用避免重复的优雅实现
问题描述
我有大量表述相近的题目,不想为每道题单独创建习题文件。考试时希望给每位学生分配其中k道题(示例中k=3),因此计划在每场考试里重复调用该习题k次。为避免学生拿到重复题目,我设置了计数器,每调用k次就重新抽取一组k道题的样本。但遇到exams2nops会运行两次代码的问题:一次预运行(用于检查习题格式、元信息),一次正式运行,导致计数器初始值直接变成2,可能出现最后一道题和前k-1道重复的情况。我目前有临时解决办法,但不够优雅,希望得到更优的实现方案。
当前使用的习题代码
data generation, echo = FALSE, results = "hide" ## parameters k <- 3 envir <- .GlobalEnv N <- envir$N if (length(N)==0) { list1 <- envir$list1 <- c("Option1","Option2","Option3","Option4","Option5","Option6","Option7","Option8","Option9","Option10","Option11","Option12","Option13","Option14","Option15") list2 <- envir$list2 <- rep(c("001","010","100"),each=5)} if (length(N)!=k) { N <- envir$N <- sample(1:15,size=k) envir$no <- 0 } # Starts the counter over again and generates a new sample whenever a rotation is through. envir$no <- envir$no+1 if (envir$no==k) N <- envir$N <- 1:100 # The 1:100 is kinda random. Just needs to be larger than any reasonable k. if (envir$no==2&length(envir$M)==0) { envir$no <- 1 envir$M <- 0 }# This is my solution to the counter issue. When the counter first hits 2, it's set back to 1, but because M exists afterwards, this is never called again.
Question ======== `r list1[N[no]]` Answerlist ---------- * A * B * C Meta-information ================ extype: schoice exsolution: `r list2[N[no]]` exname: questions exshuffle: 3
优化方案
核心思路
exams2nops的预运行阶段不会生成实际输出,此时环境变量EXAMS_OUTPUT为空;正式生成试卷时,该变量会被设置为输出文件的路径。我们可以通过判断这个环境变量,区分预运行和正式运行,避免预运行干扰计数器和样本的初始化。同时,改用更清晰的状态管理逻辑,减少全局环境的混乱。
优化后的代码
data generation, echo = FALSE, results = "hide" ## 参数设置 k <- 3 # 判断是否为正式运行阶段 is正式运行 <- nzchar(Sys.getenv("EXAMS_OUTPUT")) # 初始化题目池和答案模板(仅第一次运行时设置) if (!exists("list1", envir = .GlobalEnv)) { list1 <<- c("Option1","Option2","Option3","Option4","Option5","Option6","Option7","Option8","Option9","Option10","Option11","Option12","Option13","Option14","Option15") list2 <<- rep(c("001","010","100"), each = 5) } if (is正式运行) { # 正式运行时才处理样本和计数器 if (!exists("current_sample", envir = .GlobalEnv) || length(current_sample) != k) { # 重新抽取k道题的样本,并重置计数器 current_sample <<- sample(1:15, size = k) counter <<- 0 } # 计数器自增 counter <<- counter + 1 # 当完成k次调用后,清除样本和计数器,下次调用会重新抽取 if (counter == k) { rm(current_sample, counter, envir = .GlobalEnv) } # 当前要使用的题目索引 current_idx <- current_sample[counter] } else { # 预运行时,返回一个占位的题目索引(避免报错) current_idx <- 1 }
Question ======== `r list1[current_idx]` Answerlist ---------- * A * B * C Meta-information ================ extype: schoice exsolution: `r list2[current_idx]` exname: questions exshuffle: 3
优化点说明
- 区分预运行与正式运行:通过
EXAMS_OUTPUT环境变量判断,预运行时仅返回占位题目,不修改计数器和样本,彻底避免预运行的干扰。 - 清晰的状态管理:用
current_sample存储当前学生的题目样本,counter记录已调用次数,逻辑直观易懂。 - 自动重置样本:完成k次调用后自动清除样本和计数器,下次调用会自动抽取新的k道题,无需手动设置无效值。
- 减少全局环境污染:仅在必要时初始化全局变量,逻辑更严谨,降低冲突风险。
内容的提问来源于stack exchange,提问作者PaperBasket
相关产品推荐
相关产品推荐

