R 的一个优势在于它能够帮助生成文档。Sweave
和 knitr
可以处理 .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!”并决定对其进行更改,但由于您的实际帮助文件很大,因此这不是使用我的示例时遇到的琐碎问题。因此,您可以执行以下操作
- 运行
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 -->
- 使用
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、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
阶段但支持 synctex
的 pdflatex
,所以我为 Sweave
和 patchDVI
添加了对它的支持。knitr
到来对 Sweave
进行了改进,并包含了一致性支持。我多次切换文本编辑器和预览器,每次都编写新脚本来连接各部分。
不幸的是,R Markdown 由 Pandoc 处理,据我所知,Pandoc 不支持任何将输入行与输出行关联起来的方法。如果我错了,我非常乐意得到纠正!因此,协和索引不适用于 R Markdown 或其他依赖于 Pandoc 的处理器,例如Quarto
。我相信roxygen2
使用 Pandoc 处理一些帮助文件,所以这也将很困难。