在下面的代码中,我想“破解”诸如此类的代码a^b
,以便改为应用\macro{a}{b}
。该解决方案应该适用于自然指数和最终索引的字母,例如{abcd}^4
或{x_1}^2
。
\documentclass{article}
\usepackage{forloop}
\newcounter{power}
\newcommand\macro[2]{%
\forloop[1]{power}{0}{\value{power} < #2}{#1\kern0.3ex}%
\kern-0.3ex%
}
\newcommand\test[1]{
% Lost in translation...
}
\begin{document}
\test{x} % ---> x
\test{x y} % ---> x y
\test{x y^2} % ---> x y y
\test{x^3 y^2 z} % ---> x x x y y z
\end{document}
答案1
该代码应该是不言自明的:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\test}{m}
{
\projetmbc_test:n { #1 }
}
\tl_new:N \l__projetmbc_test_tl
\cs_new_protected:Nn \projetmbc_test:n
{
\tl_set:Nn \l__projetmbc_test_tl { #1 }
\regex_replace_all:nnN
{ (\cB. .*? \cE.|[[:alpha:]])\^ } % search a braced group or single letter followed by ^
{ \c{projetmbc_power:nn} \1 } % prepend \projetmbc_power:nn and remove ^
\l__projetmbc_test_tl
\ensuremath { \tl_use:N \l__projetmbc_test_tl }
}
\cs_new:Nn \projetmbc_power:nn
{
\prg_replicate:nn { #2 } { #1 }
}
\ExplSyntaxOff
\begin{document}
\test{x} % ---> x
\test{x y} % ---> x y
\test{x y^2} % ---> x y y
\test{x^3 y^2 z} % ---> x x x y y z
\test{{x_1}^3 {abcde}^2}
\end{document}
答案2
这是一个基于 LuaLaTeX 的解决方案。
% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{amsmath} % for '\ensuremath' macro
\usepackage{luacode} % for 'luacode' env. and '\luastringN' macro
\begin{luacode}
function test ( s )
s = s:gsub ( "(\\%a+) ^(%d+)", string.rep ) -- e.g., '\alpha^3'
s = s:gsub ( "(%a)^(%d+)" , string.rep ) -- e.g., 'x^2'
s = s:gsub ( "(%b{})^(%d+)" , string.rep ) -- e.g., '{x_1}^4'
tex.sprint ( s )
end
\end{luacode}
% Define a LaTeX wrapper macro:
\newcommand\test[1]{\directlua{test(\luastringN{#1})}}
\begin{document}
\obeylines
\test{$x$}
\test{$x y$}
\test{$x^1 y^12$}
\test{$x^3 y^2 z$}
\test{${x_1}^3 {abcde}^2$} % courtesy of @egreg's posting
\test{$\alpha^2\lambda^3\omega^4$}
\end{document}
答案3
expl3
您还可以通过逐个吸收标记来实现完全扩展。这会忽略空格,但由于您计划在数学模式下将其用于偏导数,所以这应该不是问题。不过,它可能相当慢。它也不能递归工作,即\test{{x^3}}
不会重复。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new:Npn \mbc_process_powers:w #1 #2 #3 {
\str_if_eq:nnF { #1 } { \q_stop }
{
\str_if_eq:nnTF { #2 } { ^ }
{
\prg_replicate:nn { #3 } { #1 }
\mbc_process_powers:w
} {
#1 \mbc_process_powers:w { #2 } { #3 }
}
}
}
\NewExpandableDocumentCommand \test { m }
{
\mbc_process_powers:w #1 \q_stop \q_stop \q_stop
}
\ExplSyntaxOff
\begin{document}
\ttfamily % nicer font for \meaning
\edef\x{\test{x}} \meaning\x % ---> x
\edef\x{\test{x y}} \meaning\x % ---> xy
\edef\x{\test{x y^2}} \meaning\x % ---> xyy
\edef\x{\test{x^3 y^2 z}} \meaning\x % ---> xxxyyz
\edef\x{\test{{abcd}^4 or {x_1}^3}} \meaning\x % ---> abcdabcdabcdabcdorx_1x_1x_1
\end{document}
如果您不能或不想使用expl3
,您也可以在普通 LaTeX 中实现它,但您需要一些辅助宏:
\makeatletter
\protected\def\@qstop{\@qstop}
\ifdefined\directlua
% LuaTeX doesn't have \pdfstrcmp.
\directlua{
function pdfstrcmp(a, b)
if a < b then
tex.sprint("-1")
elseif a > b then
tex.sprint("1")
else
tex.sprint("0")
end
end
}
\long\def\pdfstrcmp#1#2{\directlua{pdfstrcmp("\luaescapestring{#1}", "\luaescapestring{#2}")}}
\fi
\def\@ifstrequal#1#2{%
\ifnum\pdfstrcmp{\unexpanded{#1}}{\unexpanded{#2}}=0
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
\def\replicate#1#2{%
\ifnum\numexpr#1\relax>0
#2%
\expandafter\replicate\expandafter{\number\numexpr(#1)-1\relax}{#2}%
\fi
}
\def\processpowers#1#2#3{%
\@ifstrequal{#1}{\@qstop}{}{%
\@ifstrequal{#2}{^}{%
\replicate{#3}{#1}%
\processpowers
}{%
#1\processpowers{#2}{#3}%
}%
}%
}
\newcommand\test[1]{\processpowers#1\@qstop\@qstop\@qstop}
\makeatother
答案4
在评论中,您询问 LuaTeX 是否也可以使用 TeX 令牌。我不会要求您解释用例,而是将其视为学术兴趣,并提供一个示例来展示如何在原则上做到这一点。
LuaTeX 带有内置token
库,提供处理和操作 TeX 标记的功能。特别是,它具有scan_toks()
允许扫描由匹配括号分隔的标记列表的功能。
\directlua{t = token.scan_toks()}{...}
调用此函数后,变量t
将包含 中的所有内容...
。令牌以 Lua 表的形式表示,您可以将令牌的属性作为该表的元素进行查询。我在这里使用的元素是
cmdname
标记所代表的内部 TeX 命令的名称tok
TeX 分配的唯一标记标识符
要比较两个标记是否相同,您可以比较它们的tok
属性(尽管以这种方式比较控制序列时必须小心,因为它们可以具有其他属性,例如\protected
、\long
或\outer
)。
token.put_next
最后,我们可以使用(它也接受一个表,在这种情况下它简单地遍历表并将每个标记放入输入流)将标记放回输入流中。
在示例中,我没有换行\directlua
,而是luacall
通过将 Lua 函数定义放入使用检索的全局函数表中lua.get_functions_table()
,然后luacall
使用在 TeX 中注册来定义token.set_lua
。这有一些好处,虽然在这里并不真正相关,但很好,例如,以\test
这种方式定义的宏只需一步即可展开。
此解决方案的一个大麻烦是 LuaTeX 标记不知道它们来自什么输入,也就是说,要检查标记是否包含数字,我们必须将其与所有结果为数字的标记进行比较。为此,我定义了一个查找表,将tok
标记的属性映射到相应的数字。
\documentclass{article}
\usepackage{luacode}
\begin{luacode}
-- Lookup table to convert tokens to numbers
local numbers = {
[token.create(string.byte("1")).tok] = 1,
[token.create(string.byte("2")).tok] = 2,
[token.create(string.byte("3")).tok] = 3,
[token.create(string.byte("4")).tok] = 4,
[token.create(string.byte("5")).tok] = 5,
[token.create(string.byte("6")).tok] = 6,
[token.create(string.byte("7")).tok] = 7,
[token.create(string.byte("8")).tok] = 8,
[token.create(string.byte("9")).tok] = 9,
[token.create(string.byte("0")).tok] = 0,
}
-- Register a new Lua function with TeX
local lft = lua.get_functions_table()
lft[#lft + 1] = function()
-- Scan a list of tokens delimited by balanced braces
local toks = token.scan_toks()
local result = {}
local stack = {}
local currentgrouplevel = 0
local n = 1
while n <= #toks do
-- We have to scan balanced braces, so we in/decrease the
-- currentgrouplevel on every brace
if toks[n].cmdname == "left_brace" then
currentgrouplevel = currentgrouplevel + 1
elseif toks[n].cmdname == "right_brace" then
currentgrouplevel = currentgrouplevel - 1
end
-- Collect tokens on a stack
table.insert(stack, toks[n])
-- If we are not inside braces, check for the ^
if currentgrouplevel == 0 then
if toks[n + 1] and toks[n + 1].cmdname == "sup_mark" and
toks[n + 2] and toks[n + 2].cmdname == "other_char" then
-- Convert the token right after ^ to a number by looking it up
local rep = assert(numbers[toks[n + 2].tok], "Token is not a number")
-- Append the stack to the result rep times
for i = 1, rep do
for _, t in ipairs(stack) do
table.insert(result, t)
end
end
-- Flush the stack
stack = {}
-- Skip the next two tokens (^ and number)
n = n + 2
else
-- We are not inside braces but there is also no ^, so we flush the stack
for _, t in ipairs(stack) do
table.insert(result, t)
end
stack = {}
end
end
-- Move on to the next token
n = n + 1
end
-- Flush whatever is still on the stack
for _, t in ipairs(stack) do
table.insert(result, t)
end
-- Put the result back into the input stream
token.put_next(result)
end
-- Bind the registered function to "test"
token.set_lua("test", #lft, "global")
-- The "global" definition (similar to gdef) is needed because the luacode*
-- environment is an implicit TeX group and set_lua obey TeX grouping
\end{luacode}
\begin{document}
\test{$x$}
\test{$x y$}
\test{$x^1 y^12$}
\test{$x^3 y^2 z$}
\test{${x_1}^3 {a{bc}de}^2$}
\test{$\{x_1\}^3$}
\test{$\alpha^2\lambda^3\omega^4$}
\end{document}