假设我想写一个这样的宏:
\makelinesegment{1:1-1:2-2:2}
并且它是否变成了类似这样的:
\draw (1,1) -- (1,2) -- (2,2);
问题是,如何将其解析1:1-1:2-2:2
为单一选项。
使用另一种语言你可以通过以下方式来解析它:
input.split('-').each(fn(frag){
let parts = frag.split(':')
# draw (parts[0], parts[1]), ...
})
想知道如何在 LaTeX 中做到这一点。
答案1
这是一个 expl3 实现。
\lineparser_make_line
应该很容易理解,它在 处拆分参数-
,执行 map 以转换a:b
为(a,b)
,然后使用\draw
与 s 连接的序列进行调用--
。坐标的拆分有点难看,因为是块:
中的字母,因此如果直接用作参数分隔符,则会产生错误的 catcode。\ExplSyntaxOn\ExplSyntaxOff
:
\documentclass{article}
\usepackage{xparse,expl3,tikz}
\ExplSyntaxOn
% We need
%\cs_new:Npn\__lineparser_transform_coords:w#1:#2\q_mark}{
% but the ":" is changed by\ExplSyntaxOn, so we have to use a trick to use the normal colon in the parameter
\use:x{\cs_new:Npn\exp_not:N\__lineparser_transform_coords:w##1\c_colon_str##2\exp_not:N\q_mark}{
(#1,#2)
}
\seq_new:N\__lineparser_coordinates_seq
\seq_new:N\__lineparser_coordinates_transformed_seq
\cs_new:Nn\lineparser_make_line:n{
\seq_set_split:Nnn\__lineparser_coordinates_seq{ - }{#1}
\seq_set_map:NNn
\__lineparser_coordinates_transformed_seq
\__lineparser_coordinates_seq
{
\__lineparser_transform_coords:w##1\q_mark
}
\draw
\seq_use:Nn
\__lineparser_coordinates_transformed_seq
{ -- }
;
}
\NewDocumentCommand\makelinesegment{m}{
\lineparser_make_line:n{#1}
}
\ExplSyntaxOff
\begin{document}
\pagenumbering{gobble}
\begin{tikzpicture}
\makelinesegment{1:1-1:2-2:2}
\end{tikzpicture}
\end{document}
与 Phelype Oleiniks 的回答类似,有特殊的函数来处理逗号列表,因此如果\makelinesegment{1:1,1:2,2:2}
要解析,你可以例如使用
\documentclass{article}
\usepackage{xparse,expl3,tikz}
\ExplSyntaxOn
% We need
%\cs_new:Npn\__lineparser_transform_coords:w#1:#2\q_mark}{
% but the ":" is changed by\ExplSyntaxOn, so we have to use a trick to use the normal colon in the parameter
\use:x{\cs_new:Npn\exp_not:N\__lineparser_transform_coords:w##1\c_colon_str##2\exp_not:N\q_mark}{
(#1,#2)
}
\seq_new:N\__lineparser_coordinates_seq
\seq_new:N\__lineparser_coordinates_transformed_seq
\cs_new:Nn\lineparser_make_line:n{
\seq_set_from_clist:Nn\__lineparser_coordinates_seq{#1}% <-- The change
\seq_set_map:NNn
\__lineparser_coordinates_transformed_seq
\__lineparser_coordinates_seq
{
\__lineparser_transform_coords:w##1\q_mark
}
\draw
\seq_use:Nn
\__lineparser_coordinates_transformed_seq
{ -- }
;
}
\NewDocumentCommand\makelinesegment{m}{
\lineparser_make_line:n{#1}
}
\ExplSyntaxOff
\begin{document}
\pagenumbering{gobble}
\begin{tikzpicture}
\makelinesegment{1:1,1:2,2:2}
\end{tikzpicture}
\end{document}
这比 Phelype Oleinik 的原始 TeX 解决方案更长更冗长,但我认为它更容易理解。
答案2
版本 1:
我的答案是主要使用 TeX 基元。我知道还有其他方法可以做到这一点,尤其是使用 expl3,但我还没有做到,所以...
我定义了一个接受一个参数的主宏\makelinesegment
,在示例中1:1-1:2-2:2
。
\makelinesegment
appends 将其参数传递给\@coordpair
,它将以形式为的某些内容作为参数x:y-
。\@coordpair
将采用x
和y
坐标并将它们添加\this@path
到(x,y)--
。
当\@coordpair
到达输入末尾时(IE找到\empty:\empty-
),它完成并返回\makelinesegment
。
到目前为止,我们已经通过(1,1)--(1,2)--(2,2)--
.then\makelinesegment
调用来从 中\gobblemm
删除最后一个。--
\this@path
最后,\makelinesegment
调用\draw\this@path;
。
\documentclass{article}
\usepackage{tikz}
\makeatletter
\def\makelinesegment#1{%
\def\this@path{}%
\@coordpair#1-\empty:\empty-\@empty%
\expandafter\gobblemm\this@path;
\draw\this@path;
}
\def\gobblemm#1--;{\edef\this@path{#1}}
\def\@coordpair#1:#2-#3\@empty{%
\ifx#1\empty
\else
\edef\this@path{\this@path(#1,#2)--}%
\expandafter\@coordpair%
\fi
#3\@empty%
}
\makeatother
\begin{document}
\pagenumbering{gobble}
\begin{tikzpicture}
\makelinesegment{1:1-1:2-2:2}
\end{tikzpicture}
\end{document}
版本 2:
正如我在评论中所说,这种表示法在使用负坐标时可能会(并且会)引起歧义。因此,我建议稍作修改:将-
分隔坐标对的 替换为,
。如下所示:1:1,1:2,2:2
。
解决方案变得更加简单,因为我们可以使用 LaTeX\@for
来迭代逗号分隔的列表。
现在\makelinesegment
宏只需将其参数传递给\@for
,后者拆分坐标对并将它们传递给\@pair
。\@pair
然后将坐标对附加(x,y)--
到\this@list
。
其余部分与第一个版本类似;\gobblemm
删除最后一个--
并\makelinesegment
调用\draw\this@path;
。
\documentclass{article}
\usepackage{tikz}
\makeatletter
\def\makelinesegment#1{%
\def\this@path{}%
\@for\@pair:=#1\do{%
\expandafter\splitpair\@pair\@empty%
}%
\expandafter\gobblemm\this@path;%
\draw\this@path;
}
\def\gobblemm#1--;{\edef\this@path{#1}}
\def\splitpair#1:#2\@empty{%
\edef\this@path{\this@path(#1,#2)--}%
}
\makeatother
\begin{document}
\pagenumbering{gobble}
\begin{tikzpicture}
\makelinesegment{1:1,1:2,2:2}
\end{tikzpicture}
\end{document}
答案3
没有提供测试文件(再次),所以我从另一个答案中借用了一个。对于这样一个简单的转换,你实际上不需要任何额外的解析代码,只需定义扩展以内联进行:
\documentclass{article}
\usepackage{tikz}
\def\makelinesegment#1{\draw\xmakelinesegment#1-\empty;}
\def\xmakelinesegment#1:#2-#3{%
(#1,#2)
\ifx\empty#3\else--\expandafter\xmakelinesegment\fi
#3}
\begin{document}
\pagenumbering{gobble}
\begin{tikzpicture}
\makelinesegment{1:1-1:2-2:2}
\end{tikzpicture}
\end{document}
答案4
由于您想自己调整解析器,这里有一个简单但具有发展性的解决方案(在@Manuel 的帮助下),使用expl3
正则表达式。这个解决方案解决了 Phelype Oleinik 提到的情况:1:1--1:-2-2:2
以下 2 个-
。
\documentclass{article}
\usepackage{tikz,xparse}
\ExplSyntaxOn
\tl_new:N \l_bob_func_tl
\NewDocumentCommand \makelinesegment { m }
{
\pgfextra
\tl_set:Nn \l_bob_func_tl { #1 }
\regex_replace_all:nnN { ([0-9])- } { \1)--( } \l_bob_func_tl
\regex_replace_all:nnN { : } { , } \l_bob_func_tl
\exp_last_unbraced:NNV
\endpgfextra
(\l_bob_func_tl)
}
\ExplSyntaxOff
\begin{document}
\tikz\draw\makelinesegment{1:1--1:2-2:2};
\end{document}