我正在重新排版《计算机程序的结构和解释》中的一张图(图3.9):
我对 TikZ 的思考结果如下:
请忽略白色主色调,我想让图片在深色和浅色背景下均可使用。
这是图片的代码:
#+name: remark-environments-3-9
#+header: :imagemagick yes :iminoptions -density 300 :imoutoptions -geometry 800
#+header: :fit yes :headers '("\\usepackage{tikz} \\usetikzlibrary{positioning,fit,petri,arrows}")
#+header: :buffer on
#+begin_src latex :results raw file :exports both :file figure-3-9.png
\begin{tikzpicture}[inner sep=0mm,>=stealth',very thick,color=black!50]
\begin{scope} [node distance=4mm]
\node (make withdraw) {make-withdraw: ...};
\node (fake1) [right=of make withdraw,xshift=40mm] {};
\node (W1) [below=of make withdraw.west,anchor=west] {W1:};
\end{scope}
\node (g env) [draw,rectangle,inner sep=2mm, fit=(make withdraw) (W1) (fake1)]
{ };
\node (g env name) [left={of g env},text width=1cm]
{global env} edge[->,very thick] (g env);
\begin{scope}[node distance=0mm]
\node [draw,minimum size=5mm,circle,tokens=1,below=of W1.east, yshift=-20mm] (w1fun-left) {};
\node [draw,minimum size=5mm,circle,tokens=1,right=of w1fun-left, xshift=0mm]
(w1fun-right) {};
\node [rectangle, fit=(w1fun-left) (w1fun-right)] (w1fun)
{} edge [<-, to path={|- (\tikztotarget)}] (W1.east);
\end{scope}
\node [below=of w1fun-left, align=left] {parameters: amount \\ body:...}
edge [<-] (w1fun-left.center);
\node (E1-env) [below=of g env,yshift=5mm,draw,inner sep=2mm] {balance: amount}
edge [->] (g env);
\node [left=of E1-env,xshift=5mm] {E1} edge [->] (E1-env);
\path (w1fun-right.center) edge[->,to path={-| (\tikztotarget)}] (E1-env.south);
\end{tikzpicture}
#+end_src
这看起来或多或少与原始代码相匹配,甚至看起来更好,但我想知道有哪些最明显(和不太明显)的方法可以使代码更符合惯用方式。
我尤其不喜欢:
- “两点”范围
- “代币”仍然是黑色的,因此不可见
- 手动 xshift 和 yshift
- 如果我想制作更大的环境图,我需要定义一些宏或类似的东西以供以后使用,以制作类似 \environment 或 \closure 的东西。
- 还有其他方法可以让它看起来更好吗?
答案1
至于你的观点:
- 您可以将此范围设为
pic
。这也解决了第 4 点。 - 您可以使用
colored tokens={black!50}
。 - 如果在定位语法中添加距离,则不需要手动移位,例如
right=44mm of make withdraw
。 - 重复事物的一个非常方便的方法是使用
pic
s。当你用手指指向它时:指向形状的箭头就是pin
s。另一个显而易见的事情是使用样式。我box
为绘制的重复矩形添加了样式。可以向这些样式添加参数,以及在没有设置参数时应用的默认值。在示例中,box
参数是inner sep
,其默认值是2mm
。 - 当不清楚目标输出是什么时,很难让事情变得更好。暂时我添加了
font=\sffamily
。
这是一个完整的示例,它还使用该backgrounds
库来添加黑色背景。如果你在第 5 点下进一步解释你的期望,我会很乐意重申。
\documentclass[tikz,border=3mm]{standalone}
\usetikzlibrary{positioning,arrows,fit,petri,backgrounds}
\begin{document}
\begin{tikzpicture}[inner sep=0mm,>=stealth',very thick,color=black!50,
background rectangle/.style={fill=black},show background rectangle,
font=\sffamily,pics/two dots/.style={code={
\node [draw,minimum size=5mm,circle,colored tokens={black!50}]
(#1-left) {};
\node [draw,minimum size=5mm,circle,colored tokens={black!50},
right=0pt of #1-left]
(#1-right) {};
\node [rectangle, fit=(#1-left) (#1-right)] (#1){};
}},
every pin edge/.style={<-,very thick},
box/.style={draw,rectangle,inner sep=#1},box/.default=2mm]
%
\node (make withdraw) {make-withdraw: \dots};
\node (fake1) [right=44mm of make withdraw] {};
\node (W1) [below=4mm of make withdraw.west,anchor=west] {W1:};
%
\node (g env) [box, fit=(make withdraw) (W1) (fake1),
pin={[text width=1cm,pin distance=10mm]left:global env}]
{ };
%
\path ([yshift=-20mm]W1.east)pic{two dots=w1fun}
(w1fun) edge [<-, to path={|- (\tikztotarget)}] (W1.east);
%
\node [below=of w1fun-left, align=left] {parameters: amount \\ body: \dots}
edge [<-] (w1fun-left.center);
\node (E1-env) [below=5mm of g env,box,
pin={[pin distance=5mm]left:E1}] {balance: amount}
edge [->] (g env);
\path (w1fun-right.center) edge[->,to path={-| (\tikztotarget)}]
(E1-env.south);
\end{tikzpicture}
\end{document}