有人能帮助我了解 LuaTeX 文档的处理顺序吗?
据我了解,顺序如下:
- 代码正在被读取,从上到下
- 当编译器找到一些 Lua 代码的开头时,它会吞下它,扩展它能找到的任何 TeX 命令,并将结果传递给 Lua 解释器
Lua 解释器解释代码并将其输出放回到文档中
输出是否被再次解析?是否直接放入文档中?
为了说明我的问题:
\newcommand{\compare}[2]{
#1
\directlua{
testNumber=#2
if #1<testNumber then
tex.sprint("is bigger")
else
tex.sprint("is smaller")
end
tex.sprint(" than "..testNumber..".")
}
}
当\compare{1}{5}
被调用时,Lua 解释器被输入:
testNumber=5
if 1<testNumber then
tex.sprint("is bigger")
else
tex.sprint("is smaller")
end
tex.sprint(" than "..testNumber..".")
并且输出将会被粘贴到调用directlua命令的位置的文档中。
现在假设我添加另一个命令:
\newcommand{\iterate}[2]{
\directlua{
for i=1,#1,1 do
tex.print(\\compare{i}{#2})
end
}
}
并通过 \iterate{5}{5} 调用它
根据我有限的理解,这应该按如下方式提供给 Lua 解释器:
for i=1,10,1 do
tex.print(\compare{i}{5})
end
输出结果为:
\compare{1}{5}
\compare{2}{5}
\compare{3}{5}
\compare{4}{5}
\compare{5}{5}
这个输出是否再次被评估?我似乎无法运行我的代码,因此我想我可能误解了一些东西。
这个过程是否可以从预处理器-处理器关系的角度来理解,比如说,一个 PHP 应用程序输出 JS 代码,而该代码本身正在由客户端解释?
答案1
当你写作时
\newcommand{\iterate}[2]{
\directlua{
for i=1,#1,1 do
tex.print(\\compare{i}{#2})
end
}
}
Lua 进程看到
for i=1,#1,1 do
tex.print(<the result of \compare>)
end
你需要做的是这样的:
\newcommand\iterate[2]{%
\luaexec{
for i=1,#1 do
tex.print(string.format("\\compare{\%d}{#2}",i))
end
}
}
\iterate{3}{2}
(luaexec 来自 luacode 包)这会将“ \compare{1}{2}
\compare{2}{2}
\compare{3}{2}
”打印到 TeX 的输入字符串中,并对其进行评估。这有效,因为\\
它会生成一个反斜杠,并且它与单词“compare”分开。这是一种 hack,我不建议这样做。请参阅如何在 LuaTeX 中执行“printline”了解更多信息。
这是一个关于“重新评估”的实验:
例如:如果你写
\documentclass[a4paper]{article}
\usepackage{luacode}
\begin{document}
\begin{luacode*}
tex.sprint("%hello")
\end{luacode*}
\end{document}
TeX 将会把%hello
它看作和解释为注释,因此不会生成输出文件。
当你这样写的时候(将 -2 视为 tex.print() 的第一个参数)
\documentclass[a4paper]{article}
\usepackage{luacode}
\begin{document}
\begin{luacode*}
tex.sprint(-2,"%hello")
\end{luacode*}
\end{document}
tex 看到 %hello,但 % 有一个“安全”类别代码。如果 tex.print 的第一个参数是数字,它将被视为 catcode 表。我想要展示的是:TeX 读取调用的结果\directlua{}
。
答案2
值得一提的是,ConTeXt 提供了一个 lua 表context
,可以访问在 TeX 级别定义的所有宏。因此,您可以使用它context.compare
来调用在 TeX 端定义的宏\compare
。因此,可以按如下方式将您的宏转换为 ConTeXt(我遵循与您的宏相同的定义。编写 lua 函数的正常 ConTeXt 风格有所不同)。
\def\compare#1#2%
{\directlua{
local testNumber=#2
if #1<testNumber then
context("#1 is bigger")
else
context("#1 is not bigger")
end
context(" than "..testNumber..".")
}}
\def\iterate#1#2%
{\directlua{
for i=1,#1,1 do
context.compare(tostring(i) , #2)
end}}
\starttext
\iterate{5}{2}
\stoptext
这使用了一些我不理解的 Lua 魔法。从内部来看,它类似于 rdhs 给出的答案;只是您不必手动进行管道操作。请参阅ConTeXt Lua 文档手册了解更多详细信息。
答案3
首先,这是您的代码的工作版本:
\newcommand{\compare}[2]{
#1
\directlua{
testNumber=#2
if #1<testNumber then
tex.sprint("is bigger")
else
tex.sprint("is not bigger")
end
tex.sprint(" than "..testNumber..".")
}
}
\newcommand{\iterate}[2]{
\directlua{
for i=1,#1,1 do
tex.print("\noexpand\\compare{" .. i .. "}{#2}")
end
}
}
\iterate{5}{5}
现在,基本事件顺序如下:
\newcommand
将您的代码存储在 的定义中\iterate
。到目前为止,尚未执行任何操作。- 在某个时候,
\iterate
会遇到并替换您的定义。TeX 会看到\directlua
并尝试扩展它。 - TeX (via
\directlua
) 扩展括号内的所有内容。这意味着参数 (#1
) 以及\\
,因为\\
是控制序列。因此当您说 时"\\compare"
,TeX尝试将 的定义传递\\
给 Lua。为了防止这种情况,您需要使用"\noexpand\\compare"
。您也可以使用[[\@backslashchar]] .. "compare"
或 ,如果脑损伤严重,请尝试"\@backslashchar\noexpand\compare"
。 \directlua
将扩展的代码传递给Lua.,使其"\\compare"
成为\compare
。- TeX 现在看到
\directlua
扩展为五个\compare
序列(及其参数)。它会替换您的定义并看到另一个\directlua
,它会尝试将其扩展确切地与步骤2相同。
您可以通过将 Lua 代码放入单独的文件或使用软件包提供的功能来“关闭”步骤 3。您可以luacode
对步骤 5 执行相同操作,tex.print(-2,"")
因为这会使 Lua 的输出变成 TeX 不会尝试继续扩展的字符串。
FWIW,这是最干净的实现方式\iterate
:
\begin{filecontents*}{myiterate.lua}
function mycompare(num1,num2)
tex.sprint(num1 .. " ")
if num1<num2 then
tex.sprint("is bigger")
else
tex.sprint("is not bigger")
end
tex.sprint(" than " .. num2 .. ".")
end
function myiterate(reps,targ)
for i=1,reps,1 do
tex.sprint([[\compare{]] .. i .. "}{" .. targ .. [[}\\]])
end
end
\end{filecontents*}
...
\directlua{require("myiterate")}
\newcommand\compare[2]{\directlua{mycompare(#1,#2)}}
\newcommand\iterate[2]{\directlua{myiterate(#1,#2)}}
\iterate{5}{5}