string.format、\directlua 和 tex.sprint 的问题

string.format、\directlua 和 tex.sprint 的问题

我想知道是否有人可以向我解释为什么

\documentclass{article}
\begin{document}
the standard approximation $\pi = \directlua{tex.sprint(math.pi)}$
\end{document}

一切正常,而

\documentclass{article}
\begin{document}
the standard approximation $\pi = \directlua{tex.sprint(string.format(' %.3f', math.pi))}$
\end{document}

抛出一个Undefined control sequence. \end{document}

这可能是非常基本的问题,但我就是不明白。提前谢谢

答案1

(百分比)符号%对于 (La)TeX 和 Lua 来说都是特殊的,但用法却大不相同。在 (La)TeX 中,它是默认注释字符。在 Lua 中,它是模式匹配操作中的“魔法”字符之一。(Lua 的其他“魔法”字符是^$().[]*+-?。)

\directlua指令是可扩展,在 TeX 特定意义上。这意味着 TeX 扫描 的参数以\directlua查找任何宏;如果找到宏,TeX 将尝试扩展它们。在您的第二个示例中,当 (La)TeX 扫描

\directlua{tex.sprint(string.format(' %.3f', math.pi))}

它没有遇到任何 LaTeX 宏,但遇到了该%字符,它将其解释为注释字符。因此,输入行末尾的所有内容都会被忽略,包括两个右括号和一个右花括号。这就是您收到错误消息的原因。


如何避免这两种互不相容的用途%互相干扰?我想到了四种策略。

以不会被 LaTeX 不当处理的方式加载 Lua 代码。

  • 将Lua代码放在外部文件中(通常以扩展名开头.lua,例如file.lua),并通过指令将代码加载到LuaTeX中\directlua{dofile("file.lua")}

  • 在你的主 tex 文件中,加载lua代码包,它提供了称为luacode和的环境luacode*,并将您的 Lua 代码放置在或luacode环境中luacode*

不要这么做,而是在执行 Lua 代码时使用可以替代的\directlua指令来处理。\%%

  • 加载luacode提供宏的包\luaexec(用来代替),并使用反斜杠\directlua“转义”所有实例,即,将它们重写为。例如,%\%

    \luaexec{tex.sprint(string.format('\%.3f', math.pi))}
    

定义一个名为 的宏,\percentchar该宏扩展为 ,而%TeX 不会将此字符解释为注释字符。这样就可以继续使用\directlua。(非常感谢 Joseph Wright 提供此建议,并感谢 @egreg 提供指向 更简洁定义的指针\percentchar!)

  • 第四种方法是让%字符在 TeX 眼中不特殊。将以下指令 --\@percentchar由 LaTeX“内核”定义;它输出 -- 的 catcode-12(“其他”)形式%放在序言中:

    \makeatletter\let\percentchar\@percentchar\makeatother
    %% or: \begingroup\catcode`\%=12\relax\gdef\percentchar{%}\endgroup 
    

    然后,在文档正文中执行此指令:

    \directlua{tex.sprint(string.format('\percentchar.3f', math.pi))}
    

    回想一下,\directlua是可扩展的,也就是说,它将扩展\percentchar%,这是 Lua 需要在string.format函数中看到的字符。而且,由于这种形式的%已被赋予 catcode 12(“其他”),因此它不会被 LaTeX 误解为注释字符——一切都很好。:-)

    虽然第四种方法乍一看似乎有点麻烦,但它具有不可否认的优点,即无需加载任何包(例如包luacode)或将 Lua 代码放在外部文件中即可实现。


究竟应该使用这四种方法中的哪一种,主要取决于在进行 Lua 调用时需要完成什么。如果只是简单指令的单个实例,第三和第四种方法可能最简单。如果 Lua 代码很长且很复杂,则几乎肯定会首选第一种和第二种方法。

以下示例说明了前三种可能性。

在此处输入图片描述

\RequirePackage{filecontents}
%% create an external file, to store Lua code
\begin{filecontents*}{aux.lua}
function printpi (n) -- n: number of decimal digits to be shown
  tex.sprint ( string.format ( '%.'..n..'f' , math.pi ))
end
\end{filecontents*}

\documentclass{article}
\directlua{ dofile ( "aux.lua" ) } % load Lua code from external file

\usepackage{luacode} % load 'luacode' package
\begin{luacode}
function pi3() -- this function doesn't take an argument
  tex.sprint ( string.format ( '%.3f' , math.pi ) )
end
\end{luacode}

\begin{document}
$\pi \approx \directlua{printpi(3)}$

$\pi \approx \directlua{pi3()}$

$\pi \approx \luaexec{tex.sprint(string.format('\%.3f', math.pi))}$
\end{document}

答案2

这是另一个想法。只需四舍五入math.pi并显示结果。Lua 没有四舍五入到n小数位的函数,但创建一个相对简单。下面的代码是 ConTeXt 的,但也应该可以直接转换为 LaTeX:

\startluacode
  local floor, ceil = math.floor, math.ceil

  math.Round = function(x, n)
      local factor = math.pow(10, n or 0)
      local y = x * factor
      if x >= 0 then 
          y = floor(y + 0.5) 
      else
          y = ceil(y - 0.5)
      end

      return y / factor
  end
\stopluacode

\starttext
$π \approx \ctxlua{context(math.Round(math.pi, 3))}$
\stoptext

答案3

我们可以在 OpTeX 中做什么:

$\pi \approx \directlua{tex.sprint(string.format('\%.3f', math.pi))}$
\bye

相关内容