如何使從 tex.print 尾遞歸呼叫的巨集?

如何使從 tex.print 尾遞歸呼叫的巨集?

考慮

\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包含印刷內容的a,\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.put_next()它們將與之後的代幣一樣保留
  • 僅適用於真實的“文件”/偽文件,而不是例如參數標記列表

1. 在延續令牌上使用 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.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].

相關內容