¿Cómo puedo hacer que las macros llamadas desde tex.print tail-recursivas?

¿Cómo puedo hacer que las macros llamadas desde tex.print tail-recursivas?

Considerar

\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}

Problema:

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

Por lo que puedo ver tex.sprintes "equivalente" a un \inputque contiene el contenido impreso, y puedes tener \input15 niveles como máximo.

Pregunta:

  • ¿Por qué eleliminación de recursividad de cola¿No funciona cuando la entrada proviene de una línea en lugar de una lista de tokens?
  • ¿Cómo solucionar el problema? Suponiendo que necesito tex.sprint(), por ejemplo, imprimir más contenido de cadena a la derecha de \F.

Mientras tanto, encuentro una solución usando token.put_next(), pongo \relax y la obtengo más tarde.

Respuesta1

Puede forzar a tex a desenrollar la pila de entrada antes de recurrir mirando hacia adelante con\expandafter

ingrese la descripción de la imagen aquí

\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}

Respuesta2

0.texio.closeinput()

Sugerido porun comentario

Código:


%! 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}

El manual explica...

Esta función debe usarse con cuidado. Actúa como \endinput pero al final de Lua. Puedes usarlo para (más o menos) forzar un salto de regreso a TeX.

lo que significa que elimina el contenido restante del "archivo" superior.

Nota/aclaración del manual,

  • no lo haceen realidadVuelva a TeX, el siguiente contenido de Lua aún se ejecuta
  • tex.print()Los comandos que lo preceden también se eliminan.
  • a diferencia \endinputdel siguiente contenido en la línea se elimina
  • peroSi hay algunos tokens pendientes, token.put_next()se conservarán tanto tiempo como los posteriores.
  • sólo se aplica al "archivo"/pseudoarchivo real en lugar de, por ejemplo, la lista de tokens de argumentos

1. Utilice token.put_next en el token de continuación.

%! 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}

(experimentalmente, todo token.put_next()viene después de todo tex.*print(), independientemente de su orden en el código)

Creo que la razón por la que este método funciona es que mientras se expande una macro, a diferencia de cuando se ejecuta token.get_next()o token.scan_toks()etc., TeX desenrolla la pila de entrada (incluso en caso de que la macro no tenga ningún argumento, como en este caso)

2. Utilice futuro

%! 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}

\immediateassignmentsolía hacerlo funcionar en un contexto de solo expansión.

Tenga en cuenta que si el siguiente token es un notexpandedtoken, se cambiará.

3. (solo funciona parcialmente, no lo use) Use token.put_next en otro token y get_next desde dentro de 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}

Esto no realiza una recursividad de cola adecuada (de alguna manera token.get_next()no elimina el nivel de entrada si está agotado), por lo que si 3000 aumenta a un valor mayor, verá

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

información relacionada