我的文档中有相当多的标签和引用(大约 250 个标签,600 个引用)。由于我需要重新排列文档的各个部分,我想知道哪些标签引用了哪些部分(以及,哪些标签被引用的标签引用等)。换句话说,我希望我的文档有一个图形表示,它只显示环境(证明、引理等)的名称/编号以及相应的标签和引用。
例子:
- 引理 2 -> 证明 A,证明 B,引理 1
- 证明 B -> 证明 C
- 引理 3 -> 引理 2
我该如何生成这样的图表?我只对图表传达的信息感兴趣,图表本身不会被发布,也不需要看起来很漂亮。
答案1
我看了看我的参考文献包,这并不容易,因为我没有写任何文档,源代码中也没有注释。所以我改进了这种情况,修复了一些错误,写了一些注释,但文档还有待完成。
rdfref
灵感来自分布式文件框架,顾名思义。其思想是,每个引用命令(\rdflabel
、\rdfref
、\rdfpageref
)都会以以下形式为当前主题添加一些属性
subject attribute object
取决于主题的类型。主题的类型要么基于冒号前的标签部分(所以对于\rdflabel{sec:hello} is the type
sec`),要么基于包含环境:
\begin{lem}\rdflabel{thm:lem1}
\partOf{thm:thr1}
A lemma. Reference to theorem\ref{thm:euclid}
\end{lem}
在这种情况下,主题类型是lem
。\partOf
是自定义命令,它指出该引理是定理的一部分thm:thr1
。
引用命令定义了另一种类型的主题,即所谓的空白节点,因此可以搜索这样的节点并对它们进行一些操作。
可以使用\AddRdfType
命令为给定的主题类型指定添加的属性:
\AddRdfType{lem}{%
\AddProperty{rdf:type}{thm:lemma}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddPropertyEx{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddProperty
属性通过和命令添加。其他一些属性通过:和\AddPropertyEx
自动保存。所有属性都保存在文件中。如果您在此文件中搜索标签,您将找到以下内容:\rdflabel
doc:hasParent
doc:envir
aux
thm:lem1
\LoadTriple {thm:lem1}{doc:hasParent}{sec:first}
\LoadTriple {thm:lem1}{doc:envir}{lem}
\LoadTriple {thm:lem1}{rdf:type}{thm:lemma}
\LoadTriple {thm:lem1}{doc:pageNo}{1}
\LoadTriple {thm:lem1}{rdfs:label}{1\ }
\LoadTriple {thm:lem1}{doc:partOf}{thm:thr1}
您可以使用 获取任何属性的值\GetPropery{subject}{property}
,因此\GetProperty{thm:lem1}{doc:pageNo}
将打印1
。
重要的属性是rdf:type
,所有主题都应分配。 的值rdf:type
应为它自己的主题,rdf:type
设置为rdfs:Class
和一些描述性rdfs:label
。也可以使用 定义类型层次结构rdfs:subClassOf
。您需要在\WithObject
命令中设置这些属性,因为这些属性只能分配一次。(\AddRdfType
每次\rdflabel
调用时都会保存属性):
\WithObject{thm:lemma}{%
\AddProperty{rdfs:label}{Lemma}
\AddProperty{rdf:type}{rdfs:Class}
\AddProperty{rdfs:subClassOf}{thm:theorem}
}
thm:theorem
在这个例子中,class 被定义为父类。
所有这些属性都应该在所谓的本体文件中声明,就像这个theorems-ontology.tex
定理文件一样:
\newcommand\partOf[1]{%
\AddPropertyEx{doc:partOf}{#1}
}
\AddRdfType{Theorem}{%
\AddProperty{rdf:type}{thm:theorem}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddProperty{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{thr}{%
\AddProperty{rdf:type}{thm:theorem}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddPropertyEx{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{prop}{%
\AddProperty{rdf:type}{thm:proposition}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddPropertyEx{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{lem}{%
\AddProperty{rdf:type}{thm:lemma}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddPropertyEx{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
\AddRdfType{sec}{
\AddProperty{rdf:type}{sec:sectioning}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddPropertyEx{rdfs:label}{\@currentlabel\ \@currentlabelname}
\AddPropertyEx{doc:label}{\@currentlabel}
}
\AddRdfType{equation}{%
\AddProperty{rdf:type}{eq:equation}
\AddPropertyEx{doc:pageNo}{\thepage}
\AddProperty{rdfs:label}{\@currentlabel\ \@currentlabelname}
}
%Add properties for classes
\WithObject{sec:sectioning}{%
\AddProperty{rdfs:label}{Sectioning}
\AddProperty{rdf:type}{rdfs:Class}
}
\WithObject{thm:theorem}{%
\AddProperty{rdfs:label}{Theorem}
\AddProperty{rdf:type}{rdfs:Class}
}
\WithObject{thm:proposition}{%
\AddProperty{rdfs:label}{Proposition}
\AddProperty{rdf:type}{rdfs:Class}
\AddProperty{rdfs:subClassOf}{thm:theorem}
}
\WithObject{thm:lemma}{%
\AddProperty{rdfs:label}{Lemma}
\AddProperty{rdf:type}{rdfs:Class}
\AddProperty{rdfs:subClassOf}{thm:theorem}
}
\WithObject{eq:equation}{%
\AddProperty{rdfs:label}{Equation}
\AddProperty{rdf:type}{rdfs:Class}
\AddProperty{rdfs:subClassOf}{thm:theorem}
}
\WithObject{rdfs:Class}{%
\AddProperty{rdfs:label}{Class}
}
现在我们可以看一下文档序言:
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[]{tgschola}
\usepackage[T1]{fontenc}
\usepackage{hyperref}
\usepackage[]{rdfref-user,rdfref-query,lipsum,nameref,cleveref}
\usepackage{amsthm,thmtools,xstring}
\declaretheorem[name=Lemma,Refname={Lemma,Lemmas}]{lem}
\declaretheorem[name=Proposition,Refname={Proposition,Proposition}]{prop}
\declaretheorem[name=Theorem,Refname={Theorem,Theorems}]{thr}
thmtools
包用于定义新的定理,如环境lem
、prop
和thr
。现在将 ontology 和 redefine\ref
命令包含到 中\rdfref
,只是为了显示它有效:
\begin{document}
\inputontology{theorem-ontology.tex}
\let\ref\rdfref
现在是一些带有定理的示例文本(我不是数学家所以这纯粹是胡说八道,我只是想展示一些可能性):
\section{Hello world}\rdflabel{sec:first}
\lipsum[1-2]
\begin{lem}\rdflabel{thm:lem1}
\partOf{thm:thr1}
A lemma. Reference to theorem \ref{thm:euclid}
\end{lem}
\begin{prop}
\rdflabel{thm:prop1}
\partOf{thm:thr1}
A proposition.
%\renewcommand{\theenumi}{\thecor.\arabic{enumi}}
\begin{enumerate}
\item An item
\item Another one
\end{enumerate}
\end{prop}
\begin{thr}\rdflabel{thm:thr1}
A theorem.
\end{thr}
\begin{thr}[Euclid]
\rdflabel{thm:euclid}%
For every prime $p$, there is a prime $p’>p$.
In particular, the list of primes,
\begin{equation}\rdflabel{eq:1}
2,3,5,7,\dots
\end{equation}
is infinite.
\end{thr}
As it was said in theorem \ref{thm:euclid}, we see that
在我们的本体中,我们还定义了sec
类型,因此添加了\section{Hello world}\rdflabel{sec:first}
主题。rdf:type
sec:sectioning
现在可以使用rdfref-query
包来做一些有趣的事情。这个包使我们能够轻松地查询我们的数据库。如果我们想搜索所有定理类主题,我们可以利用它们都具有rdf:type
属性这一事实,这些属性是类的子类thm:theorem
。我们可以创建一个简单的命令来查找具有rdfs:subClassOf
=thm:theorem
属性的所有主题,并创建将来可以使用的列表:
\newcommand\buildtypelist[2]{%
\listadd#1{#2} % add searched
\Bind{?x}{rdfs:subClassOf}{#2}{%
% only direct children of #2 are taken into account
\listxadd#1{\GetVal{?x}}% must be global and expanded
}%
}
%
\buildtypelist\theoremlist{thm:theorem}
\Bind
命令非常有用。它需要四个参数,其中一些可能是未知值(以 开头?
),这些值在搜索期间绑定。前三个参数是subject
、attribute
和object
,第四个参数是针对每个找到的值执行的函数体。
因此,在这种情况下,将处理所有rdfs:subClassOf
等于的主题。的命令用于构建列表。 要获取绑定变量的值,必须使用命令。thm:theorem
etoolbox
\listxadd
\GetVal
构建列表后,我们可以使用 etoolbox 的列表循环命令处理所有定理:
\newcommand\MakeLabel[1]{%
\Bind{#1}{rdf:type}{?type}{% get type of parent
\textbf{\GetValProperty{?type}{rdfs:label}} % parent type label
}%
% now clickable reference to parent
\hyperref[\GetVal{#1}]{\GetValProperty{#1}{rdfs:label}}%
}
\renewcommand\do[1]{%
\Bind{?obj}{rdf:type}{#1}{%
% Prin basic info about the object
\par Object type:
\textbf{\GetValProperty{#1}{rdfs:label}}, % #1 is theorem type label
% now clickable reference to label of current object
value:\textbf{\hyperref[\GetVal{?obj}]{\GetValProperty{?obj}{rdfs:label}}},
% and just label
label: [\GetVal{?obj}].
% Print info about parent object:
\Bind{?obj}{doc:hasParent}{?par}{% parent label
\par \hspace{2em}Parent object:%
\MakeLabel{?par}%
}%
\def\refinfo{\par\hspace{2em} Referenced by:\par\hspace{3em}}
\Bind{?ref}{doc:refersTo}{?obj}{%
\refinfo\def\refinfo{\par\hspace{3em}}%
\GetValProperty{?ref}{rdfs:label} in
\Bind{?ref}{doc:hasParent}{?par}{%
\MakeLabel{?par}%
}, p. \hyperpage{\GetValProperty{?ref}{doc:pageNo}};%
}%
}}
\dolistloop\theoremlist
这是一个复杂的例子,它循环遍历所有定理类型。\Bind{?obj}{rdf:type}{#1}
将循环遍历给定类型的所有定理。引入了新命令\GetValProperty
,它获取绑定变量的属性。辅助宏\MakeLabel
用于打印对象的标签。它用于打印有关父主题的信息(\Bind{?obj}{doc:hasParent}{?par}
)。使用搜索给定定理的参考资料\Bind{?ref}{doc:refersTo}{?obj}
。
完整示例文件可在以下位置找到:rdfref 页面. 结果信息:
现在我们可以转到图表:首先定义一些辅助宏:
% Now export a theorem graph
% Make expandable label
\newcommand\MakePlainLabel[1]{%
\GetProperty{\GetProperty{#1}{rdf:type}}{rdfs:label}
\GetProperty{#1}{rdfs:label}
}
我们将使用\MakePlainLabel
为节点制作标签
% Make list of used nodes, every node should be added only once
% #1 list csname #2 node prefix #3 subject
\newcommand\AddNode[3]{%
\ifcsdef{node@lbl@#2@#3}{}{%
\listxadd#1{#3}
\csgdef{node@lbl@#2@#3}{\MakePlainLabel{#3}}
}%
}
我们只需要添加一次标签,所以我们定义一个列表并只添加每个主题一次
% replace colons with underscores
\def\coltoun#1:#2;{#1_#2}% graphwiz doesn't support colon in names
\def\graphrelations{}
% connect nodes
% #1 source node #2 destination #3 graphviz style
\newcommand\AddRelation[3]{%
\edef\tempa{#1}%
\edef\tempb{#2}%
% add node label. we can call \AddNode for node multiple times,
% node is beeing added only once
\AddNode\mynodes{myn}{#1}%
\AddNode\mynodes{myn}{#2}%
% store relations in a macro
\xdef\graphrelations{%
\graphrelations \expandafter\coltoun\tempa; -> \expandafter\coltoun\tempb; [#3];^^J
}%
}
\AddRelation
如果尚未使用,将添加节点标签,并定义两者之间的关系。
现在我们可以制作实际的图表了。思路如下:循环遍历所有定理并将它们与父对象连接起来。对于与\partOf
命令连接的定理,使用doc:partOf
属性,否则使用doc:hasParent
。然后循环遍历对给定定理的所有引用,获取父对象并用虚线将它们连接到定理。
\renewcommand\do[1]{%
\Bind{?obj}{rdf:type}{#1}{%
% select parent node. for lemmas etc, which use doc:partOf property,
% use that, otherwise use doc:has:parent
\IfProperty{\GetVal{?obj}}{doc:partOf}{%
\edef\parnode{\GetValProperty{?obj}{doc:partOf}}
}{
\edef\parnode{\GetValProperty{?obj}{doc:hasParent}}
}
\AddRelation{\GetVal{?obj}}{\parnode}{}
\Bind{?ref}{doc:refersTo}{?obj}{%
% select references to a theorem and print parent subject
\def\parnode{\GetValProperty{?ref}{doc:hasParent}}
\AddRelation{\parnode}{\GetVal{?obj}}{style=dotted}
}
}
}
% again loop over al theorems
\dolistloop\theoremlist
现在我们需要一些宏来存储
graph in graphviz format to a file:
% now prepare output file for the graphviz output
\newwrite\graphwrite
% macros for opening and writing to the graph file
\newcommand\graphopen[1]{%
\immediate\openout\graphwrite=#1%
}
\newcommand\graphout[1]{\immediate\write\graphwrite{#1}}
\graphopen{\jobname.dot} % graphviz file name will be the same
% as input file name with .dot extension
\graphout{digraph hello\@charlb} % save graph header
% save labels
\renewcommand\do[1]{%
\graphout{\coltoun#1; [label="\MakePlainLabel{#1}"]}
}
\dolistloop\mynodes
% save graph relations
\graphout{\graphrelations}
\graphout{\@charrb} % save footer
经过 LaTeX 处理文档后,文件filename.dot
保存为:
digraph hello{
thm_thr1 [label="Theorem 1\ "]
sec_first [label="Sectioning 1\ Hello world "]
thm_euclid [label="Theorem 2\ Euclid "]
thm_lem1 [label="Lemma 1\ "]
thm_prop1 [label="Proposition 1\ "]
eq_1 [label="Equation 1\ "]
thm_thr1 -> sec_first [];
thm_euclid -> sec_first [];
thm_lem1 -> thm_euclid [style=dotted];
sec_first -> thm_euclid [style=dotted];
thm_prop1 -> thm_thr1 [];
thm_lem1 -> thm_thr1 [];
eq_1 -> thm_euclid [];
}
你可以用 graphviz 来编译它:
dot -Tpng filename.dot -o filename.png
以及结果图: