Как сделать макросы, вызываемые из 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содержащего печатный контент, и вы можете использовать \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. Используйте фьючерлет

%! 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используется для работы только в контексте расширения.

Обратите внимание, что если следующий токен является notexpandedтокеном, он будет изменен.

3. (работает только частично, не используйте) Используйте token.put_next на другом токене и get_next его изнутри Lua

%! 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].

Связанный контент