a^b ---> \macro{a}{b}

a^b ---> \macro{a}{b}

在下面的代码中,我想“破解”诸如此类的代码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 命令的名称
  • tokTeX 分配的唯一标记标识符

要比较两个标记是否相同,您可以比较它们的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}

在此处输入图片描述

相关内容