
Учитывать
\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].