在竞技编程中评估 TeX 输出

在竞技编程中评估 TeX 输出

在线评委是竞技编程中使用的平台。参赛者编写解决问题的代码。参赛者将他们的程序提交给在线评委,评委在相同的预定输入(如果有)和环境下运行评分服务器中的所有代码。评委将程序的输出与正确输出进行比较,并确定参赛者是否解决了任务。

当我被迫放弃我最喜欢的语言,因为主要的编码竞赛不支持它时,我感到很沮丧。现在我正在做一个项目,将尽可能多的语言纳入在线评判,重点关注不受欢迎的语言。最近我成功地整合了 *sh 和 powershell。在寻找新的候选语言时,我想到了 TeX 家族。这将是该小组的一个绝佳补充。

我的计划是编译、整理所有 PDF 页面,将其整理成一张长图像,然后对其进行 OCR,然后将其与正确的输出进行比较。也就是说,pdflatex | pdftocairo | tesseract | diff然而,我遇到了许多挑战。由于 TeX 写入具有长度限制的 PDF,因此无法正确判断需要用户输出长行文本的问题。

例如,考虑一个基本问题,要求参赛者在一行中打印 1 到 100 的数字,每个数字之间用一个空格隔开。使用 C 语言,可以这样做:

$ cat ./example.c
#include <stdio.h>

int main() {
    for (int i = 1; i <= 100; i++) {
        printf("%d ", i);
    }
    return 0;
}

代码将按照如下流程进行判断:

$ gcc ./example.c -o ./example.o
$ ./example.o
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

这将会有所不同,在线裁判会判定它是正确的。然而,在 LaTeX 中,输出将不可避免地跨越多行,导致 ocr 输出时不同。格式在竞争性编程中很重要,这不能被视为正确的。我无法修改 tesseract 参数以假设所有输出都在一行中,因为这会破坏任何需要两行以上输出的问题。对 TeX 给予豁免对其他语言是不公平的。

$ cat ./example.tex
\documentclass{article}
\usepackage{pgffor}
\pagenumbering{gobble}
\begin{document}
\foreach \i in {1,...,100} {%
    \i\space%
}%
\end{document}
$ pdflatex ./example.tex
$ pdftocairo ./example.pdf -jpeg -singlefile
$ tesseract -c debug_file=/dev/null ./example.jpg -
123456789 1011 12 13 1415 1617 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

不仅输出跨越多行,而且 tesseract 错误地将数字 1 到 9 组合成单个字符串。是否有可行的方法来调整此过程,以便为 TeX 提供一个可靠的自动评分系统,类似于其他既定语言,不会给它带来劣势或优势?

如果这不可行,唯一的其他方法就是让 TeX 输出到 stdout 或纯文本文件(就像所有其他方法一样)。可以指示 TeX 执行此操作吗?

答案1

\documentclass{article}
\usepackage{pgffor}
\pagenumbering{gobble}
\newwrite\res
\immediate\openout\res=\jobname.txt
\begin{document}
\def\x{}
\foreach \i in {1,...,100} {%
    \xdef\x{\x\i\space}
}%
\immediate\write\res{\x}
\end{document}

留下一行文本文件.txt扩展名,包含

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 

答案2

下面创建一个与 .tex 文件同名但扩展名为 .txt 的文件,其中包含从 1 到 100 的所有数字,后跟一个空格。

本例使用了L3语言,它为TeX添加了一个连贯的编程语言(仍然通过宏扩展)。

\ExplSyntaxOn

\iow_open:Nn \g_tmpa_iow { \jobname.txt }
\tl_clear:N \l_tmpa_tl
\int_step_inline:nn { 100 } { \tl_put_right:Nn \l_tmpa_tl { #1~ } }
\iow_now:Nx \g_tmpa_iow { \l_tmpa_tl }
\iow_close:N \g_tmpa_iow

\stop

结果是

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 

\tl_put_right:Nn另一种可能性是使用函数来代替多次调用\tl_build_...,这为逐段构建标记列表提供了更好的性能。

\ExplSyntaxOn

\iow_open:Nn \g_tmpa_iow { \jobname.txt }
\tl_build_begin:N \l_tmpa_tl
\int_step_inline:nn { 100 } { \tl_build_put_right:Nn \l_tmpa_tl { #1~ } }
\tl_build_end:N \l_tmpa_tl
\iow_now:Nx \g_tmpa_iow { \l_tmpa_tl }
\iow_close:N \g_tmpa_iow

\stop

稍微短一点的代码变体,使用可扩展函数来映射从 1 到 100 的整数,并使用可扩展宏添加空格(当涉及到内容是否可扩展时,TeX 可能会很奇怪,因此如果您有更复杂的事情要做,这可能是在写入文件时更难使用的变体):

\ExplSyntaxOn

\iow_open:Nn \g_tmpa_iow { \jobname.txt }
\cs_new:Nn \my_addspace:n { #1~ }
\iow_now:Nx \g_tmpa_iow { \int_step_function:nN { 100 } \my_addspace:n }
\iow_close:N \g_tmpa_iow

\stop

还有完全可扩展且性能更佳的版本:

\ExplSyntaxOn

\iow_open:Nn \g_tmpa_iow { \jobname.txt }
\cs_new:Npn \my_spaceloop:nN #1#2
  { #1~ \exp_args:Ne #2 { \int_eval:n { #1 + 1 } } }
\iow_now:Nx \g_tmpa_iow
  {
    \exp_args:NNNo \exp_last_unbraced:NNo \my_spaceloop:nN 1
      { \prg_replicate:nn {99} \my_spaceloop:nN }
    \use_none:n
  }
\iow_close:N \g_tmpa_iow

\stop

答案3

其他答案会更改输入文档,其中一个答案比另一个答案更激进一些。这可能不是编程竞赛所希望的,因为在编程竞赛中,你会希望评判一些“自然”的代码。

除了编译为 pdf,您还可以使用 编译为 html tex4ht。这样输入相同,并生成一个包含纯文本的 html 文件,这可能比多行 PDF+OCR 等更容易区分。

基本示例:

example.tex

\documentclass{article}
\usepackage{pgffor}
\pagenumbering{gobble}
\begin{document}
\foreach \i in {1,...,100} {%
    \i\space%
}%
\end{document}
make4ht example.tex

结果example.html

<!DOCTYPE html> 
<html lang='en-US' xml:lang='en-US'> 
<head><title></title> 
<meta charset='utf-8' /> 
<meta content='TeX4ht (https://tug.org/tex4ht/)' name='generator' /> 
<meta content='width=device-width,initial-scale=1' name='viewport' /> 
<link href='example.css' rel='stylesheet' type='text/css' /> 
<meta content='example.tex' name='src' /> 
</head><body>
<!-- l. 7 --><p class='noindent'>1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
100
</p>
    
</body> 
</html>

当然,执行 for 循环来打印数字列表并不是很像 TeX 的东西,因此对于编程竞赛来说,这不是一个好选择。您还可以为参与者分配一个数学练习。为此,您可以在输出中选择不适合自动评判的图像格式、MathML 或 MathJax。

例子:

\documentclass{article}
\usepackage{pgffor}
\pagenumbering{gobble}
\begin{document}
\foreach \i in {1,...,100} {%
    \i\space%
}%

$c = \sqrt{a^2+b^2}$
\end{document}

MathML 的终端命令:

make4ht example.tex "mathml"

生成的 html(片段,经过简单格式化以提高可读性):

<!-- l. 9 --><p class='indent'>   <!-- l. 9 -->
<math display='inline' xmlns='http://www.w3.org/1998/Math/MathML'>
<mrow>
   <mi>c</mi> <mo class='MathClass-rel'>=</mo>
   <msqrt>
      <mrow><msup><mrow><mi>a</mi></mrow>
      <mrow><mn>2</mn> </mrow> </msup> 
      <mo class='MathClass-bin'>+</mo> <msup><mrow><mi>b</mi></mrow>
      <mrow><mn>2</mn></mrow></msup></mrow>
   </msqrt>
</mrow>
</math>
</p>

MathJax 的终端命令:

make4ht example.tex "mathjax"

HTML 片段:

<!-- l. 9 --><p class='indent'>   \(c = \sqrt {a^2+b^2}\)
</p>

如果您想让解析/比较文件变得更容易一些,那么您可以在 LaTeX 文件中引入一个或多个环境,并为class与该练习相对应的输出部分分配一个 html 属性。这需要为 TeX4ht 提供单独的配置文件。

mycompetition.cfg

\Preamble{xhtml}
\ConfigureEnv{assignment}
    {\HCode {<div class="assignment">}} 
    {\HCode {</div>}} {} {}
\begin{document}
\EndPreamble

example.tex

\documentclass{article}
\newenvironment{assignment}{}{}
\usepackage{pgffor}
\pagenumbering{gobble}
\begin{document}
\begin{assignment}
\foreach \i in {1,...,100} {%
    \i\space%
}%
\end{assignment}

\begin{assignment}
$c = \sqrt{a^2+b^2}$
\end{assignment}
\end{document}

终端命令:

make4ht -c mycompetition.cfg example.tex "mathjax"

Html输出片段:

<!-- l. 6 --><p class='noindent'><div class='assignment'> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
100
</div>
</p><!-- l. 12 --><p class='noindent'><div class='assignment'> \(c = \sqrt {a^2+b^2}\)
</div>
</p>

您可以尝试扩展它以包括例如分配计数器作为环境的参数,请参阅TeX4ht:如何使用参数配置新环境?以获得一些关于如何处理这一问题的指示。

答案4

编辑:以下方法不起作用为了达到预期目的,因为 Pandoc 不会真正评估所有的 tex(除非我遗漏了什么),因此例如foreach示例中的不会通过 Pandoc 呈现数字……

除非目标是测试 Latex 格式的细粒度技能,否则我认为最好的输出格式是 Markdown - 你可以使用 Pandoc 进行转换(参见示例 5https://pandoc.org/demos.html)markdown 输出的理想特性是

  1. 几乎是纯文本,因此很容易区分
  2. 对不同的 Latex 编码风格有一定的鲁棒性 - 即对 Latex 源代码的轻微更改会创建略有不同的 PDF 或在 HTML 输出中添加额外的 <div> 标签,它们仍然可以产生完全相同的 Markdown 输出

您可能需要进行设置--wrap=preserve以避免输出换行到 80 个字符宽度(这是默认值)

相关内容