我怎样才能使从 tex.print tail-recursive 调用的宏?

我怎样才能使从 tex.print tail-recursive 调用的宏?

考虑

\documentclass[12pt]{article}
\usepackage{luacode}
%\usepackage{luacr}
%\usepackage{miscellaneous}
\begin{document}

\global\def\F{\directlua{f()}}
\begin{luacode*}
i=0
function f()
    if i<30 then
        i=i+1
        tex.sprint(i .. [[, \F]])
    end
end
\end{luacode*}
\F

\end{document}

问题:

TeX capacity exceeded, sorry [text input levels=15].

据我所知tex.sprint“相当于”\input包含打印内容,\input最多可以有 15 个级别。

问题:

  • 为什么尾部递归消除当输入来自一行而不是一个标记列表时不起作用?
  • 如何修复该问题?假设我需要tex.sprint()在 的右侧打印更多字符串内容\F

与此同时,我找到了一种解决方法,使用token.put_next(),放置一个 \relax 并稍后获取它。

答案1

您可以通过以下方式强制 tex 在递归之前展开输入堆栈:\expandafter

在此处输入图片描述

\documentclass[12pt]{article}

%\usepackage{luacr}
%\usepackage{miscellaneous}
\begin{document}

\def\F{\expanded{\noexpand\directlua{f()}\expandafter}}
\directlua{
i=0
function f()
    if i<30 then
        i=i+1
        tex.sprint(i .. [[, \string\F]])
    end
end
}
\F

\end{document}

答案2

0。texio.closeinput()

建议来自一条评论

代码:


%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
\begin{document}

\def\F{\directlua{f()}}
\begin{luacode*}
i=0
function f()
    if i~=0 then
        texio.closeinput()
    end
    if i<20000 then
        i=i+1
        tex.sprint(i .. [[, \F]])
    end
end
\end{luacode*}
\F

\end{document}

手册中解释了...

应谨慎使用此函数。它的作用相当于 \endinput,但在 Lua 端。您可以使用它(某种程度上)强制跳转回 TeX。

这意味着它会删除最顶层“文件”的剩余内容。

手册的注释/说明,

  • 它没有真的跳回 TeX,下面的 Lua 内容仍然执行
  • tex.print()其前面的命令也会被删除
  • \endinput与以下内容不同,该行被删除
  • 如果有一些待处理的 token 被放置,token.put_next()它们将和其后的 token 一样被保留
  • 仅适用于真实“文件”/伪文件,而不是参数标记列表

1. 在延续 token 上使用 token.put_next

%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
\begin{document}

\def\F{\directlua{f()}}
\begin{luacode*}
i=0
function f()
    if i<20000 then
        i=i+1
        tex.sprint(i .. [[, ]])
        token.put_next(token.create("F"))
    end
end
\end{luacode*}
\F

\end{document}

(实验中,所有都token.put_next()出现在后面tex.*print(),无论它们在代码中的顺序如何)

我认为这种方法有效的原因是,在扩展宏时,与执行token.get_next()token.scan_toks()等时不同,TeX 会展开输入堆栈(即使宏没有任何参数,例如在这种情况下)

2. 使用 Futurelet

%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
%\usepackage{miscellaneous}
%\tracingmacros=1
\begin{document}

\def\F{\directlua{f()}}
\begin{luacode*}
i=0
function f()
    if i<20000 then
        i=i+1
        tex.sprint(i .. [[, \immediateassignment\futurelet\a\F]])
    end
end
\end{luacode*}
\expanded{\F}
\end{document}

\immediateassignment用于使其在仅扩展环境中工作。

注意,如果后面的token也是notexpandedtoken,那么就会被改变。

3. (仅部分有效,请勿使用)在另一个 token 上使用 token.put_next 并从 Lua 内部对其进行 get_next

%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
%\usepackage{miscellaneous}
%\tracingmacros=1
\begin{document}

\def\F{\directlua{f()}}
\begin{luacode*}
i=0
function f()
    token.get_next()  -- the relax token, either the original one or result of put_next
    if i<3000 then
        i=i+1
        tex.sprint(i .. [[, \F]])
        token.put_next(token.create("relax"))
    end
end
\end{luacode*}
\F\relax
\end{document}

这不会进行正确的尾递归(token.get_next()如果用尽,则不会消除输入级别),因此如果将 3000 增加到更大的值,您会看到

TeX capacity exceeded, sorry [input stack size=5000].

相关内容