在 Plain LuaTeX 中使用 \directlua 进行计算

在 Plain LuaTeX 中使用 \directlua 进行计算

我以前没有用过Lua,所以对计算有一些疑问:

  1. .0如果结果是整数,如何避免追加?
  2. 如何才能实现四舍五入后添加正确数量的零?
  3. 我怎样才能将小数点标记改为.逗号,

我已尝试过的(MWE):

% Plain LuaTeX
\catcode`\@=11

% From latex.ltx
\long\def\@ifnextchar#1#2#3{%
    \let\reserved@d=#1%
    \def\reserved@a{#2}%
    \def\reserved@b{#3}%
    \futurelet\@let@token\@ifnch}
\def\@ifnch{%
    \ifx\@let@token\@sptoken
    \let\reserved@c\@xifnch
    \else
    \ifx\@let@token\reserved@d
    \let\reserved@c\reserved@a
    \else
    \let\reserved@c\reserved@b
    \fi
    \fi
    \reserved@c}
\def\:{\let\@sptoken= } \:  % this makes \@sptoken a space token
\def\:{\@xifnch} \expandafter\def\: {\futurelet\@let@token\@ifnch}

% seen somewhere on tex.stackexchange.com
\directlua{
function math.round_int ( x )
  return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
end
function math.round ( x , n )
  return ( math.round_int ( x*10^n ) / 10^n )
end
}

% My macros
\def\c@lc[#1]#2{\directlua{tex.print(math.round(#2,#1))}}
\def\calc{\@ifnextchar[\c@lc{\c@lc[2]}} % Default is rounding to 10^-2

Let's try the macros:

$ \sqrt2 \approx \calc[4]{2^(1/2)} $ is OK.

$ \root5\of{32} = \calc{32^(1/5)} $ should be $2$ as it is an integer.

$ 1.699999 \approx \calc[3]{1.699999} $ should be $1.700$ because it is rounded to $10^{-3}$.

$ 1.9 \approx \calc[0]{1.9} $ should be $2$ as it is rounded to whole numbers.

$ 8 \approx \calc[-1]{8} $ should be $10$ as it is rounded to $10^1$.

\bye

在此处输入图片描述

答案1

你问,

.0如果(舍入运算的)结果是整数,我该如何避免附加?

在 Lua 5.3 之前的版本中,只有一种数字数据类型(“浮点数”);然而,Lua 被编程来显示数字没有小数部分不带尾数.0,即,好像它们是整数(当然,它们是整数)。Lua 5.3 版引入了一种称为“整数”的新数字数据类型;Lua 5.3 版于今年早些时候(2019 年)被纳入 LuaTeX。这一变化的一个副作用是,现在.0只有在以下情况下才会显示整数而不带尾数类型是“整数”。(当然,Lua 5.3 提供了将数字从 类型转换float为 类型的方法integer。以下代码中就用到了其中一种方法。)

那么,您遇到的问题是由于 Lua 函数math.round(由 调用\c@lc)始终返回“浮点”类型的数字;因此后面有.0。要显示整数而不带.0,我建议您进行两处更改。首先,添加以下代码块

function math.myround ( x , n )
  if n==0 then
    return ( math.round_int ( x ) )
  elseif n<0 then
    return ( math.floor ( math.round ( x , n ) ) )
  else 
    return ( math.round ( x , n ) )
  end
end

到第一个调用的参数\directlua。第二个变化

\def\c@lc[#1]#2{\directlua{tex.print(math.round(#2,#1))}}

\def\c@lc[#1]#2{\directlua{tex.print(math.myround(#2,#1))}}

与 有何math.myround不同?如果 的第一个参数为 0,即,如果要执行四舍五入到最接近的整数math.round,则函数直接math.myround调用。此外,如果 的第一个参数为负数,它会将浮点数强制转换为整数(在调用 之后)。这样, 返回的四舍五入数字就是math.round_int\c@lcmath.round\c@lcmath.myround类型 integer如果n为 0 或负数,即四舍五入后的数字实际上是一个整数。


你还问过,

如何才能实现四舍五入后添加正确数量的零?

如果四舍五入到小数点后的数字后x等于(其中是整数,也就是正整数),则运行1.7nn

string.format ( "%."..n.."f" , x )

创建一个n在小数点标记后有明确数字的字符串——其中一些可能是零。

笔记:%由于的第一个参数中有一个符号string.format,因此您无法通过 加载代码,\directlua因为 TeX 会因该符号而严重卡住%。您必须将 Lua 代码放入名为 的外部文件中,然后myround.lua通过 指令加载该文件\directlua{dofile("myround.lua")}


你问的第三个问题是,

我怎样才能将小数点标记改为.逗号,

Lua 提供了一个非常强大的函数,称为string.gsub。(gingsub代表“全局”。)您可以更改

    return ( math.round ( x , n ) )

在函数math.myround

    return ( ( string.gsub ( math.round ( x , n ) , "%." , "{,}" ) )

请注意,.不是改为,而是改为{,};这样做是为了告知 TeX 它不应该将这些实例视为,类型的字符math-punct

与上文相同:%由于的参数中有一个符号string.gsub,因此您不能通过 加载代码\directlua(因为 TeX 会再次因该符号而痛苦地卡住%)。我建议您将 Lua 代码存储在名为 的外部文件中,然后myround.lua通过 指令加载该文件\directlua{dofile("myround.lua")}


以下解决方案解决了您提出的所有三个问题。它使用 LaTeX 而不是 Plain-TeX,主要是因为我不知道如何filecontents在 Plain-TeX 设置中使用环境(这对于创建外部文件非常有用)。不过,请参阅下文以获取有关 Plain-TeX 解决方案的说明。

在此处输入图片描述

% !TEX TS-program = lualatex
\documentclass{article} % or some other suitable document class

 % The next instruction requires LaTeX format 2019-10-01 (or more recent)
\begin{filecontents*}[overwrite]{myround.lua}
-- First two functions seen somewhere on tex.stackexchange.com.
-- (Maybe https://tex.stackexchange.com/a/468118/5001 ??)

function math.round_int ( x )
   return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
end

function math.round ( x , n )
   return math.round_int ( x*10^n ) / 10^n 
end

-- New:
function math.myround ( x , n )
   if n==0 then
      return math.round_int ( x )
   elseif n<0 then
      return math.floor ( math.round ( x , n ) )
   else   -- n>0
      x = math.round ( x , n ) -- replace 'x' with rounded version
      x = string.format ( "%."..n.."f" , x )
      return ( x:gsub ( "%." , "{,}" , 1 ) )
   end
end
\end{filecontents*}

\directlua{dofile("myround.lua")} % Load the externally-stored code

\newcommand\calc[2][0]{% default value of opt. param.: 0
   \directlua{tex.sprint(math.myround(#2,#1))}}

\begin{document}
\obeylines
Let's try the macros:
$ \sqrt{2} \approx \calc[4]{2^(1/2)} $. OK.
$ \sqrt[5]{32} = \calc{32^(1/5)} $. OK.
$ 1.699999 \approx \calc[3]{1.699999} $. OK.
$ 1.699999 \approx \calc[1]{1.699999} $. Also OK.
$ 1.9 \approx \calc{1.9} $. OK.
$ 8 \approx \calc[-1]{8} $. OK.
\end{document}

附录:为了完整起见,Plain-TeX 解决方案如下所示:

  • 首先,创建一个名为的外部文件myround.lua,并将上面显示的环境内容filecontents(3 个 Lua 函数)放置在其中。

  • \directlua其次,删除示例代码中第一条指令的内容,因为该代码现在包含在外部文件中。

  • 三、更换

    % My macros
    \def\c@lc[#1]#2{\directlua{tex.print(math.round(#2,#1))}}
    \def\calc{\@ifnextchar[\c@lc{\c@lc[2]}} % Default is rounding to 10^-2
    

    \directlua{dofile("myround.lua")} % load the externally-stored code
    
    % My macros
    \def\c@lc[#1]#2{\directlua{tex.print(math.myround(#2,#1))}}
    \def\calc{\@ifnextchar[\c@lc{\c@lc[0]}} % Default rounding: to nearest integer
    

相关内容