同义词表

R 的一个优势在于它能够帮助生成文档。Sweaveknitr 可以处理 .Rnw 文件,评估 R 代码的结果并自动将其插入,以在 .tex 文件中生成 LaTeX 文档。我们称之为“预处理”,因为最初设计后来的步骤时假设 .tex 文件由用户直接编辑,然后进行处理以生成 PDF 或其他输出格式。R Markdown(使用 knitr)对用 Markdown 语言编写的文档执行相同操作。

预处理器的难点在于,在后来的步骤中出现的错误将生成错误消息,这些消息引用中间文件:例如,LaTeX 错误将引用 .tex 文件,而不是作为真实源的 .Rnw 文件。从帮助文件中生成的 HTML 代码中的错误由 HTML Tidy 实用程序根据其在 .html 文件中的行报告,而不是用户最初编写的 .Rd.R 文件。

同义词表解决了此问题。同义词表是中间文件中的行与输入文件中的行之间的映射。如果 LaTeX 或 HTML Tidy 在 "file:line" 处报告错误,则同义词表允许将该位置转换为 .Rnw.Rd 文件中的相应位置。多年前,我在 Sweave 中添加了同义词表,并编写了 patchDVI 包以将它们与预览器一起使用并转换 LaTeX 错误消息。(请参阅以下历史记录中的详细信息。)随着 R 即将发布的 4.3.0 版本,同义词表已扩展到帮助文件。HTML Tidy 的消息将同时报告 .html 文件位置和 .Rd 文件位置。

例如,文件 hello.Rd 可以包含此代码

\name{hello}
\alias{hello}
\title{Hello, World!}
\usage{
hello()
}
\description{
Prints 'Hello, world!'.

\out{<foobar>}
}

倒数第二行将文本 <foobar> 插入输出中。这不是合法的 HTML 令牌,HTML Tidy 会发出警告。有了新的更改,警告将显示为

* checking HTML version of manual ... NOTE
Found the following HTML validation problems:
hello.html:25:1 (hello.Rd:10): Error: <foobar> is not recognized!
hello.html:25:1 (hello.Rd:10): Warning: discarding unexpected <foobar>

这表明 HTML Tidy 在 hello.html 文件的第 25 行第 1 列发现了错误标记,并且该行源自 hello.Rd 的第 10 行。生成手册的 PDF 版本时也可能报告错误;目前,这些错误不会由 R 自动翻译,但如下所示,可以手动找到位置。

同义词代码

同义词代码主要用于内部使用,但已向程序包编写者提供。一个可能能够使用它的程序包是 roxygen2;除其他事项外,它还会从 .R 源文件创建帮助文件。新代码允许它将自己的同义词嵌入到 .Rd 文件中,以便 HTML Tidy 报告对 .R 文件中真实源的引用。(由于 Pandoc 限制,生成该同义词时会遇到一些难题,因此这可能不会很快发生。)

有关新代码的一些详细信息

有一个名为 "Rconcordance" 的新类,以及 tools 程序包导出的三个相关函数。"Rconcordance" 对象是具有三个字段的简单列表

  • offset:如果输出文件只有一部分与输入文件相关,则可以跳过初始 offset 行。
  • srcLine:这是从第 offset + 1 行开始的输出文件的一系列行对应的原始源文件中的行号向量。
  • srcFile:在简单的情况下,这是源文件的单个文件名;在更复杂的情况下,它可以是与 srcLine 长度相同的多个文件名,可能为每行提供不同的源文件。该类有一个 print 方法
library(tools)
concordance <- structure(list(offset = 5,
                  srcLine = 20:30,
                  srcFile = "myHelpfile.Rd"),
             class = "Rconcordance")
concordance
##          srcFile srcLine
## 6  myHelpfile.Rd      20
## 7  myHelpfile.Rd      21
## 8  myHelpfile.Rd      22
## 9  myHelpfile.Rd      23
## 10 myHelpfile.Rd      24
## 11 myHelpfile.Rd      25
## 12 myHelpfile.Rd      26
## 13 myHelpfile.Rd      27
## 14 myHelpfile.Rd      28
## 15 myHelpfile.Rd      29
## 16 myHelpfile.Rd      30

行标签是输出行号,列给出每个行对应的源文件名和行。

"Rconcordance" 对象的 as.character 方法将它们转换为一个或多个相当紧凑的字符串,适合包含在最终文档中。例如,

conc_as_char <- as.character(concordance)
conc_as_char
## [1] "concordance::myHelpfile.Rd:ofs 5:20 10 1"

as.Rconcordance 函数是一个泛型函数,定义了一个默认方法。该方法在其输入中查找类似于上述字符串的字符串,并将它们全部组合成一个同义词对象。例如

newconcordance <- as.Rconcordance(conc_as_char)
newconcordance
##          srcFile srcLine
## 6  myHelpfile.Rd      20
## 7  myHelpfile.Rd      21
## 8  myHelpfile.Rd      22
## 9  myHelpfile.Rd      23
## 10 myHelpfile.Rd      24
## 11 myHelpfile.Rd      25
## 12 myHelpfile.Rd      26
## 13 myHelpfile.Rd      27
## 14 myHelpfile.Rd      28
## 15 myHelpfile.Rd      29
## 16 myHelpfile.Rd      30

最后,tools::matchConcordance 函数将中间文件中的位置翻译为源文件中的位置。例如,在校对 HTML 帮助文件时,您可能注意到 hello.html 文件的第 1、19 和 23 行上的“Hello, world!”并决定对其进行更改,但由于您的实际帮助文件很大,因此这不是使用我的示例时遇到的琐碎问题。因此,您可以执行以下操作

  1. 运行 tools::Rd2HTML("hello.Rd", concordance = TRUE)。这将打印帮助页面的 HTML 源代码,以
<!-- concordance::hello.Rd:3 19 0 1 4 1 0 3 1 2 0 1 -6 1 0 1 1 3 0 1 7 1 0 1 1 5 0 -->
  1. 使用
concordance <- tools::as.Rconcordance("<!-- concordance::hello.Rd:3 19 0 1 4 1 0 3 1 2 0 1 -6 1 0 1 1 3 0 1 7 1 0 1 1 5 0 -->")
  1. 使用以下方法查找与第 1、19 和 23 行对应的源代码
tools::matchConcordance(c(1, 19, 23), concordance)
##      srcFile    srcLine
## [1,] "hello.Rd" "3"    
## [2,] "hello.Rd" "3"    
## [3,] "hello.Rd" "8"

前两行源自 \title{} 规范,第三行源自 \description 部分的一行文本。

古代历史

多年前,我使用 Sweave 撰写论文、演示文稿、考试等。它将 .Rnw 文件作为输入,并生成 .tex 文件作为输出。我将这些文件通过 latex 运行以获取 .dvi 文件,我可以预览、打印或转换为 PDF 以进行分发。

在那些日子里,存在预览器,它允许您单击预览中的特定单词,它们会告诉您的文本编辑器跳转到 .tex 文件中的对应位置。这有点好,但也有点烦人:我必须找出 .Rnw 文件中的正确位置来进行编辑,或者在 .tex 文件中进行编辑,并在下一次运行时被 Sweave 擦除时感到沮丧!

我解决此问题的第一个办法是在 R 2.5.0 中获取 Sweave,以保留 .Rnw 文件和它生成的 .tex 文件之间的对应关系记录,我称之为“一致性”。给定 .tex 文件中的一行,就可以找到 .Rnw 文件中的对应行。通过将此记录嵌入 latex 输出中,可以使其自动进行。我编写了 patchDVI 软件包来修改 .dvi 文件中的链接,以便预览器自动跳转到正确文件中的正确位置。太棒了!

多年来,发生了很多发展。我开始使用跳过 .dvi 阶段但支持 synctexpdflatex,所以我为 SweavepatchDVI 添加了对它的支持。knitr 到来对 Sweave 进行了改进,并包含了一致性支持。我多次切换文本编辑器和预览器,每次都编写新脚本来连接各部分。

不幸的是,R Markdown 由 Pandoc 处理,据我所知,Pandoc 不支持任何将输入行与输出行关联起来的方法。如果我错了,我非常乐意得到纠正!因此,协和索引不适用于 R Markdown 或其他依赖于 Pandoc 的处理器,例如Quarto。我相信roxygen2使用 Pandoc 处理一些帮助文件,所以这也将很困难。