我想学习expl3
编程。为了熟悉语法,我想创建一些虚拟程序,例如计算素数。但是,我无法定义一个应该返回值的函数bool
。
我正在使用\prg_new_conditional:Nnn
创建一个函数来测试某个数字是否为素数。当我尝试在此函数内声明变量(例如使用)时\int_set:Nn
,我得到了一堆“缺失数字,视为零”错误。
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new:Nn \my_primes_up_to:n
{
\int_step_inline:nnn {2} {#1}
{
\bool_if:nTF { \my_is_prime_p:n { ##1 } }
{ ##1,~ }
{}
}
}
\prg_new_conditional:Nnn \my_is_prime:n { p }
{
%\int_set:Nn \l_tmpa_int { 3 } This causes an error
\int_if_odd:nTF { #1 } % This is just a dummy test
{ \prg_return_true: }
{ \prg_return_false: }
}
\NewDocumentCommand{ \printPrimes }{ m }
{
{\noindent\Large\bfseries Primes~up~to~#1:\par\medskip}
\my_primes_up_to:n {#1}
}
\ExplSyntaxOff
\begin{document}
\printPrimes{18}
\ExplSyntaxOn
%\cs_meaning:N \my_is_prime_p:n
\ExplSyntaxOff
\end{document}
我做错了什么?我不太明白“谓词必须可扩展”是什么意思,也不太明白这意味着什么条件。
谓词是否仅用于简单测试,不能像 Java 布尔函数那样使用?如果是这样,那么编程此类行为的推荐方法是什么?我应该bool
在内部函数中设置一个变量\bool_set
吗?这需要调用内部函数并检查外部函数中的变量。或者有没有什么奇特的方法可以做到这一点?
也许我编写虚拟函数的方法不是最明智的,因为编程不需要这种行为LaTeX
。
此外,我非常感谢大家对expl3
语法方面的其他错误或不常见用法提出的建议。这实际上是我的第一个程序 :)
答案1
中的布尔表达式expl3
通过扩展工作,因此它们只能在实现中包含可扩展函数:这些函数在 中用星号标记interface3.pdf
。最重要的是,赋值是不是可扩展:因此你不能用它们来实现谓词。能实现不可扩展的分支测试,例如\tl_if_eq:nn(TF)
使用\prg_new_protected_conditional:Nnn
答案2
Joseph Wright 已经指出你不能让 TeX在作为第一个参数
\int_set:Nn \l_tmpa_int { 3 }
处理的条件/宏内执行操作:\bool_if:nTF
\bool_if:nTF
对处于扩展阶段的事物进行评估 — — 用 Donald E. Knuth 的比喻来说,TeX 是一个有眼睛、嘴巴和消化道的有机体,在 TeX 的食道中发生,就像一个“反刍”的过程 — — 并“期望”由此获得一个标记\prg_return_true:
/ \prg_return_false:
。
\int_set:Nn
意味着分配。分配不会在扩展阶段发生,而是在 TeX 处理的后期阶段发生,此时标记已经到达 TeX 的胃中。
扩展的同时还有调用\my_is_prime:n
和传递的情况意味着除了其中一个标记/之外的例程还会遇到由 触发的形成赋值的不可扩展标记。\int_set:Nn
\bool_if:nTF
\my_is_prime:n
\bool_if:nTF
\prg_return_true:
\prg_return_false:
\int_set:Nn
interface3.pdf 包含注释
TeXhackers 注释:与 等不同,bool 数据类型不是使用
\iffalse
/基元实现的,在纯 TeX、LaTeX 2ε 等中。程序员不应根据对实现的任何特定期望来使用 bool 开关。\iftrue
\newif
我还没有仔细研究布尔数据类型的实现。我猜想在扩展链中\bool_if:nTF
内部使用\prg_return_true:
/ ,在某个阶段应该会产生一些 TeX-\prg_return_false:
⟨数字⟩-数量,并且由于形成触发的分配的不可扩展标记而被破坏\int_set:Nn
,因此产生! Missing number, treated as zero.
-错误。
expl3 旨在让您更轻松地控制扩展。因此,我很高兴看到\exp_args:Nno
/ \exp_args:Nnf/
... 函数,它可以与 结合使用\use:n
。
通过这种方式,您可以通过嵌套调用来操作应一个接一个放置的参数,就像放置在 之后的
\exp_args:Nno\use:n{...}{further to-be-manipulated argument}
参数一样。最内层表达式的参数 包含宏标记,用于放置在所有(从而应用于所有)被操作的参数之前。{...}
\use:n
{...}
\exp_args:Nno\use:n{...}{further argument}
例如,
\cs_new:Npn \foo {foo}
\cs_new:Npn \bar {bar}
代码
\exp_args:Nno\use:n{
\exp_args:Nno\use:n{
\macro
}{\bar}
}{\foo}
产量:
\use:n{
\exp_args:Nno\use:n{
\macro
}{\bar}
}{foo}
产量:
\exp_args:Nno\use:n{
\macro
}{\bar}
{foo}
产量:
\use:n{
\macro
}{bar}
{foo}
产量:
\macro
{bar}
{foo}
如果您坚持使用 expl3 而不是 Lua,那么为了好玩,您可以执行可扩展的 Eratosthenes 来枚举素数。
以下实现只是一个起点,需要优化:
- 可处理的数字范围
\int_eval:n
不是那么大。 - 发生平方后数字的范围会减小。
- 出现分裂会使进程减慢。
正如我所说,这只是为了好玩。如果它只涉及\numexpr
/可处理的数字范围\int_eval:n
,那么在实际场景中,我绝对不会实现计算素数,而是实现从硬编码的表/列表中查找素数。
\ExplSyntaxOn
\cs_new:Nn \__UD_eratosthenes_mainloop:nnnn {
% #1 - <remaining list of numbers from which primes shall be sieved>
% #2 - <upper bound>
% #3 - <list of primes already found>
% #4 - <command for printing list of primes>
\int_compare:nNnTF {(\tl_head:n {#1})*(\tl_head:n {#1})}>{#2}
{ \exp_end:#4{#3#1} }{
\exp_args:Nnf \use:n { \__UD_eratosthenes_newlist:nnnwn {\use_i:nn} #1{!}!{}{#2} }
{ \exp_args:Nnf \use:n {#3}{\tl_head:n {#1}} }{#4}
}
}
\cs_new:Npn \__UD_eratosthenes_newlist:nnnwn #1#2#3#4!#5 {
% #1 = \use_i:nn/\use_ii:nn - fork which test to perform
% #2 = <prime number whose multiples are to be eliminated>
% #3 = <smallest number from remaining list of numbers from which primes shall be sieved>
% #4 = <remaining list of numbers from which primes shall be sieved>
% #5 = <new list of numbers from which primes shall be sieved>
\exp_args:Nf \str_if_eq:nnTF {\tl_head:n {#3}} {!}
{\__UD_eratosthenes_mainloop:nnnn{#5}}{
#1 {\int_compare:nNnTF {(#2)*(#2)}>{#3}\use_ii:nn\use_i:nn}
{\int_compare:nNnTF {((#3)/(#2))*(#2)}={#3}}
{
#1{\__UD_eratosthenes_newlist:nnnwn{\use_ii:nn}{#2}{#3}}
{\__UD_eratosthenes_newlist:nnnwn{#1}{#2}}#4!{#5}
}
{\__UD_eratosthenes_newlist:nnnwn{#1}{#2}#4!{#5{#3}}}
}
}
\cs_new:Nn \__UD_eratosthenes_numberlist:n {
\int_compare:nNnTF {#1} > {1}
{ \exp_args:Nno \use:n {\exp_args:No \__UD_eratosthenes_numberlist:n } {\int_eval:n {#1-1}}{#1} }
{ \exp_end: }
}
\cs_new:Npn \primeslist #1#2 {
\exp:w \exp_args:Nno \use:nn {\exp_end:} {
% Hide things in braces because the user might supply #2/<tokens> that otherwise might
% disturb tabulars/alignments etc.
\exp:w \int_compare:nNnTF {#1} < {2}
{ \exp_end: #2{} }{
\int_compare:nNnTF {#1} = {2}
{ \exp_end: #2{{#1}} }
{
\exp_args:No \__UD_eratosthenes_mainloop:nnnn
{\exp:w \__UD_eratosthenes_numberlist:n {#1}}
{#1} {} {#2}
}
}
}
}
% \primeslist{<upper bound>}{<tokens>} after two expansion-steps yields:
% <tokens>{{2}{3}...{p_k; p_k < upper bound <= p_(k+1)}}
\cs_new:Npn \prettyprintcommand #1#2#3#4 {
% #1 tokens before each item
% #2 tokens behind each item
% #3 tokens between items
% #4 list of items formed by undelimied arguments
% Items must not contain "!" which is the case for decimal numbers represented by arabic numerals.
\__UD_prettyprintloop:nnnnn{#1}{#2}{#3}{}#4{!}
}
\cs_new:Nn \__UD_prettyprintloop:nnnnn {
\exp_args:Nf \str_if_eq:nnTF {\tl_head:n {#5}} {!}
{}{ #4#1#5#2 \__UD_prettyprintloop:nnnnn{#1}{#2}{}{#3#4} }
}
\ExplSyntaxOff
\message{^^J}
\message{^^J\primeslist{1}{<tokens in front of prime-list>}}
\message{^^J}
\message{^^J\primeslist{2}{<tokens in front of prime-list>}}
\message{^^J}
\message{^^J\primeslist{100}{<tokens in front of prime-list>}}
\message{^^J}
\message{^^J\primeslist{100}{\prettyprintcommand{(}{)}{; }}}
\message{^^J}
\stop
终端输出:
<tokens in front of prime-list>{}
<tokens in front of prime-list>{{2}}
<tokens in front of prime-list>{{2}{3}{5}{7}{11}{13}{17}{19}{23}{29}{31}{37}{41
}{43}{47}{53}{59}{61}{67}{71}{73}{79}{83}{89}{97}}
(2); (3); (5); (7); (11); (13); (17); (19); (23); (29); (31); (37); (41); (43);
(47); (53); (59); (61); (67); (71); (73); (79); (83); (89); (97)
答案3
你可以尝试我的functional
包。它为 提供了直观的 LaTeX2 接口expl3
,并且复合函数(包括条件)的求值是从内到外的,这与其他现代编程语言(如 )类似Lua
。
使用此包,每个函数都通过\Result
命令传递其返回值。条件只是一个特殊函数,它返回\cTrueBool
或\cFalseBool
。
\documentclass{article}
\usepackage{functional}
\IgnoreSpacesOn
\PrgNewFunction \MyPrimesUpTo {m} {
\TlClear \lTmpaTl
\IntStepInline {2} {1} {#1} {
\BoolVarIfTF {\MyIsPrime {##1}} {\TlPutRight \lTmpaTl {##1,~}} { }
}
\Result {\Value \lTmpaTl}
}
\PrgNewConditional \MyIsPrime {m} {
\BoolSetTrue \lTmpaBool
\IntStepInline {2} {1} {#1-1} {
\IntCompareTF {\IntMathMod {#1} {##1}} = {0}
{ \BoolSetFalse \lTmpaBool } { }
}
\Result { \lTmpaBool }
}
\NewDocumentCommand \PrintPrimes {m} {
{\noindent\Large\bfseries Primes ~ up ~ to ~ #1:\par\medskip}
\MyPrimesUpTo {#1}
}
\IgnoreSpacesOff
\begin{document}
\PrintPrimes{18}
\end{document}