You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:使用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

优化点说明

  1. 区分预运行与正式运行:通过EXAMS_OUTPUT环境变量判断,预运行时仅返回占位题目,不修改计数器和样本,彻底避免预运行的干扰。
  2. 清晰的状态管理:用current_sample存储当前学生的题目样本,counter记录已调用次数,逻辑直观易懂。
  3. 自动重置样本:完成k次调用后自动清除样本和计数器,下次调用会自动抽取新的k道题,无需手动设置无效值。
  4. 减少全局环境污染:仅在必要时初始化全局变量,逻辑更严谨,降低冲突风险。

内容的提问来源于stack exchange,提问作者PaperBasket

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 01:44:56