我有两个长度相等的字符串,假设为abc
和def
(实际上它们可以更长)。两个字符串都只包含字母a-z
,A-Z
(没有数字,没有特殊字符等)。我需要从两个字符串中分别提取字符,并在我定义的另一个宏中成对使用它们。
例如对于两个字符串abc
和def
以及我定义的其他一些宏\printchar[2]{#1,#2}
,我希望能够将字符一对一对地传递到中\printchar
,即
\printchar{a}{d}
\printchar{b}{e}
\printchar{c}{f}
也许是某种 for 循环。所以我想这个问题有两个方面:
- 如何从中提取字符二逐个字符地输入等长字符串,并且
- 随后在宏内成对使用它们。(并且它应该循环遍历字符串中的所有字符)。
伪代码:
\documentclass[]{article}
\newcommand\printchar[2]{#1,#2}
\newcommand\foo[2]{% Takes in two strings
% <begin loop>
% Extracts out one character from both strings
\printchar{#1}{#2}%
% Continue loop until strings run out of characters
% <end loop>
}
\begin{document}
\foo{abc}{def} % Should print a,d,b,e,f
\end{document}
通过一些搜索,我思考也许该xstring
包可以完成第一部分?但我不太清楚如何将其与 for 循环集成以实现我想要的效果。
答案1
没有任何软件包:
\documentclass[]{article}
\makeatletter
\newcommand\printchar[2]{#1,#2}
\def\parsefoo#1#2,#3#4|{%
\ifx\relax#1\relax%
\else%
\ifx\relax#3\relax%
\else%
\printchar{#1}{#3}%
\ifx\relax#2\relax%
\else%
\ifx\relax#4\relax%
\else%
,\edef\@footmp{#2,#4|}%
\expandafter\parsefoo\@footmp%
\fi%
\fi%
\fi%
\fi%
}
\newcommand\foo[2]{% Takes in two strings
% <begin loop>
% Extracts out one character from both strings
\edef\@footmp{#1,#2|}%
\expandafter\parsefoo\@footmp%
%\printchar{#1}{#2}%
% Continue loop until strings run out of characters
% <end loop>
}
\makeatother
\begin{document}
\foo{abc}{def} % Should print a,d,b,e,f
haha
\end{document}
答案2
当任一字符串用完项目时,循环将停止。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\foo}{mmm}
{% #1 is the macro to use, #2 is the first string, #3 is the second string
\troy_foo:Nnn #1 { #2 } { #3 }
}
\seq_new:N \l_troy_foo_first_seq
\seq_new:N \l_troy_foo_second_seq
\cs_new_protected:Nn \troy_foo:Nnn
{
% split the first string into items
\seq_set_split:Nnn \l_troy_foo_first_seq { } { #2 }
% split the second string into items
\seq_set_split:Nnn \l_troy_foo_second_seq { } { #3 }
% map over the two string
\seq_mapthread_function:NNN \l_troy_foo_first_seq \l_troy_foo_second_seq #1
}
\ExplSyntaxOff
\newcommand{\printchars}[2]{#1#2\par}
\begin{document}
\foo{\printchars}{abc}{def}
\end{document}
完全可扩展的变体:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\loopstrings}{mmm}
{
\troy_loopstrings:Nnn #1 { #2 } { #3 }
}
\cs_new:Nn \troy_loopstrings:Nnn
{
\__troy_loopstrings:Nffff #1
{ \tl_head:n { #2 } } { \tl_head:n { #3 } }
{ \tl_tail:n { #2 } } { \tl_tail:n { #3 } }
}
\cs_new:Nn \__troy_loopstrings:Nnnnn
{
\bool_lazy_or:nnF { \tl_if_empty_p:n { #2 } } { \tl_if_empty_p:n { #3 } }
{
#1{#2}{#3}
\troy_loopstrings:Nnn #1 { #4 } { #5 }
}
}
\cs_generate_variant:Nn \__troy_loopstrings:Nnnnn { Nffff }
\ExplSyntaxOff
\newcommand{\printchars}[2]{#1#2\par}
\begin{document}
\loopstrings{\printchars}{abcd}{1234}
\loopstrings{\printchars}{abc}{1234}
\loopstrings{\printchars}{abcd}{123}
\end{document}
答案3
这是一个基于 LuaLaTeX 的解决方案。(与 @egreg 的答案的输出完全相同,因此没有单独的屏幕截图。)
%% to be compiled under LuaLaTeX
\documentclass{article}
\usepackage{luacode}
\begin{luacode}
function string_pair ( u , v )
for i=1,string.len(u) do
tex.sprint ( string.sub (u,i,i) .. string.sub (v,i,i) .. "\\par" )
end
end
\end{luacode}
\newcommand\foo[2]{\directlua{string_pair(\luastring{#1},\luastring{#2})}}
\begin{document}
\foo{abc}{def}
\end{document}
答案4
当我在玩弦乐的时候(循环遍历字符串)我还做了一个宏来循环两个字符串。但是,从我收到的回复来看,这似乎不是一个好的解决方案,因为它不使用尾部递归。另一方面,它的优点是易于理解,因此我把它放在这里。
\documentclass{article}
\begin{document}
\def\doubleloop<#1#2><#3#4>{%
\ifx\relax#1 \else
\ifx\relax#3 \else
[#1,#3]
\doubleloop<#2><#4>
\fi
\fi}
\def\loopstrings#1#2{\doubleloop<#1\relax><#2\relax>}
\verb|\loopstrings{abcd}{1234}| \loopstrings{abcd}{1234}
\verb|\loopstrings{abc}{1234}| \loopstrings{abc}{1234}
\verb|\loopstrings{abcd}{123}| \loopstrings{abcd}{123}
\end{document}
这可以使用尾递归转换为宏:
\documentclass{article}
\begin{document}
\makeatletter
\def\doubleloop<#1#2><#3#4>{%
\ifx#1\relax
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{\doubleloopaux{#1}{#2}{#3}{#4}}%
}
\def\doubleloopaux#1#2#3#4{%
\ifx#3\relax
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{[#1,#3]\doubleloop<#2><#4>}%
}
\makeatother
\def\loopstrings#1#2{\doubleloop<#1\relax><#2\relax>}
\verb|\loopstrings{abcd}{1234}| \loopstrings{abcd}{1234}
\verb|\loopstrings{abc}{1234}| \loopstrings{abc}{1234}
\verb|\loopstrings{abcd}{123}| \loopstrings{abcd}{123}
\end{document}