带有逐字参数的宏

带有逐字参数的宏

是否可以定义一个宏\foo,使其可以接受逐字代码,例如:\foo{cos(16 % 2)}而无需先将它们解释为 latex 代码?我知道我可以使用环境来实现这一点,但我也想定义一个简单的命令。我更喜欢基于 xparse 的解决方案,但我也愿意接受其他东西。

梅威瑟:

\documentclass{article}

\usepackage{robust-externalize} % you might need to copy the .sty from https://github.com/leo-colisson/robust-externalize

\robExtConfigure{
  enable fallback to manual mode, % avoid error if shell escape is forgotten/not used, print instead the command in the pdf
  new preset={my python exec}{
    python,
    custom include command={\mytmpvalue},
    add import={import math},
    set placeholder={__ROBEXT_MAIN_CONTENT__}{write_to_out(r"\gdef\mytmpvalue{" + str(__ROBEXT_MAIN_CONTENT_ORIG__)+ r"}")}
  },
}

\begin{document}

You can compute simple values like \cacheMe[my python exec]{math.sqrt(43)} % will not compile if you replace 43 with 43 % 2, as % is a comment in latex.

\end{document}

编辑v参数似乎没有按预期工作,也许我遗漏了一些愚蠢的东西(我检查过,#1 似乎已被缓存文件中的空字符串替换)...也许与我正在使用的 xsim 包不兼容?

\documentclass{article}

\usepackage{robust-externalize}

\robExtConfigure{
  enable fallback to manual mode, % avoid error if shell escape is forgotten/not used, print instead the command in the pdf
  new preset={my python exec}{
    python,
    custom include command={\mytmpvalue},
    add import={import math},
    set placeholder={__ROBEXT_MAIN_CONTENT__}{write_to_out(r"\gdef\mytmpvalue{" + str(__ROBEXT_MAIN_CONTENT_ORIG__)+ r"}")}
  },
}

\NewDocumentCommand{\myPython}{v}{%
\begin{CacheMeCode}{my python exec}
#1
\end{CacheMeCode}
}

\begin{document}

Why is this printing the actual content instead of the result: \myPython{math.sqrt(42 % 9)}??

Compare with:
\begin{CacheMeCode}{my python exec}
math.sqrt(42 % 8)
\end{CacheMeCode}

  
\end{document}

% Local Variables:
% TeX-command-extra-options: "--shell-escape -halt-on-error"
% End:

在此处输入图片描述

编辑2

问题似乎已经解决了,我实际上可以将内容放入 latex3 字符串中,这足以满足我的需求。不过,我很好奇为什么上述解决方案不起作用。

编辑3 事实上,如果我把字符串逐字逐句地放进去,这个问题并没有得到解决

\ExplSyntaxOn
\str_new:N \__robExt_tmp_contain_code_str
\NewDocumentCommand{\robExtCacheMeCode}{O{}+v}{%
  {% Group
    %% We store the input in a non-string element for efficiently implementing "auto forward"
    \str_set:Nn \__robExt_tmp_contain_code_str {#2}
    \str_show:N \__robExt_tmp_contain_code_str
}}

然后,如果输入包含换行符,它们实际上会被替换,^^M而我找不到如何用适当的换行符替换它们。

答案1

如果\myPython在内部调用,则收集 xparse- -type-argument 并将其传递给\cacheMe可能就足够了。\myPythonv\cacheMe

如果\myPython在内部调用环境CacheMeCode,我建议定义\myPython抓取v-type-argument 并将其放在逐字(!) 短语和之间,然后将整个内容传递给。 (当然,使用这种方法时,短语不能出现在的参数中。)\begin⁠{CacheMeCode}⁠{my python exec}⁠⟨newline⟩⟨newline⟩⁠\end{CacheMeCode}⁠⟨newline⟩⁠%\scantokens
\end{CacheMeCode}\myPython

一些评论:

  • LaTeX 获取属于 -type-argument 的标记的“机制”会v更改分配给字符 – 空格、、、、、、、、、和的类别代码,然后再让 TeX 对.tex -input-characters 进行标记以获取构成 -type -argument 的标记。(Plain TeX还会更改分配给 – 标题开头和–垂直制表符的类别代码 )。更改类别代码是因为作为-type-arguments 的组成部分,这些字符不应具有特殊功能,而应与任何其他普通字符/字母一样处理。但是分配给字符 – 水平制表符的类别代码(通常为 10(空格))不会被此机制更改。这意味着通常在标记化期间,处理属于 -type-argument 的水平制表符字符会产生类别 10(空格)和字符代码 32(!)的显式字符标记。但是,32 不是水平制表符的字符代码,而是空格字符的字符代码。更改字符代码的原因是:当对 .tex-input 中不是控制序列标记名称的组成部分的字符进行标记时,TeX 会将类别代码 10(空格)的字符规范化为类别 10(空格)和字符代码 32 的明确字符标记。为了防止在标记过程中将水平制表符规范化为字符代码 32,请在让 TeX 读取和标记 -type -argument 之前将水平制表符类别代码指定为 12(其他)。␣/[SP]\{}$&#^_%~v\dospecials[SOH][VT]v[HT]vv

  • 为了通过模式分配为水平制表符分配类别代码 12(其他),您需要以某种方式在 TeX 的内部字符表示方案中表示水平制表符的代码点编号。\catcode⟨number⟩=⟨number⟩

    (传统 8 位 TeX 引擎的内部字符表示方案是基于用一组 8 位/一个字节来表示单个字符。由于一个字节有 2^8=256 种可能的形式,因此可以表示 256 个不同的字符,而这些字符又可以在 0 到 255 的数字范围内分配不同的字符代码。在 8 位 TeX 引擎中,字符代码 0 到 127 是根据美国信息交换标准代码 (ASCII) 分配给字符的。在 8 位 TeX 引擎中,字符代码从 128 到 255 可用于使 TeX 适应非英语环境。

    TeX 引擎 LuaTeX 和 XeTeX 的内部字符表示方案基于 unicode,ASCII 是其严格子集。也就是说,可能的字符代码范围是 unicode 的代码点数范围,并且字符代码根据 unicode 标准分配给字符。通常,这些引擎将字符的字符代码/字符的 unicode 代码点数表示为位/字节序列的转换格式为 utf-8。)

    一方面,我喜欢通过 TeXbook 中 TeX 语法的 Backus-Naur 表示法所称的 ⟨字母常量⟩ 来表示 TeX 内部字符表示方案中字符的代码点编号,其中 ⟨字母常量⟩ 由显式字符标记组成,后面跟着一个单字母控制序列标记,其中形成控制序列标记名称的字母是需要获取其代码点编号的字符,后面跟着 ⟨一个可选空格⟩。`12

    例如,在 TeX 收集 ⟨number⟩ 的上下文中,`\j␣表示字符在 TeX 内部字符表示方案中的代码点的编号j

    例如,在 TeX 收集 ⟨number⟩ 的上下文中,表示 TeX 水平制表符的内部字符表示方案中的代码点的编号。`\⟨horizontal tab character⟩

    另一方面,我不喜欢直接在 .tex 输入文件中输入水平制表符,因为在编辑器中查看 .tex 输入文件时,显示带有水平制表符的内容可能会引起混淆。因此,我倾向于在 .tex 输入文件中通过 TeX 的^^-notation 表示水平制表符。^^-notation 是一种仅以可打印字符的形式提供字符替代表示的方法。在 TeX 的^^-notation 中,水平制表符可以表示为^^I:水平制表符具有代码点编号9在 TeX 的内部字符表示方案中,第九拉丁字母表中的字母是 I,其代码点数(十进制)为 64+9=73 在 TeX 的内部字符表示方案中。

    因此,在 TeX 收集 ⟨number⟩ 的上下文中,`\^^I␣表示在 TeX 内部字符表示方案中表示为 的字符的代码点的编号^^I,即水平制表符的编号。

  • TeX 从 .tex-input-file 逐行读取输入。即,当 TeX 需要从 .tex-input-file 读取时,总是会读取整行。当 TeX 需要标记时,它会对该行中的字符进行标记。关键点是:每当 TeX 从文件中读取一行时,它都会进行一些预处理,甚至在标记之前也是如此。在此预处理阶段,将删除行右端的所有空格字符,并且 - 如果的值\endlinechar在 TeX 内部字符编码方案的代码点编号范围内 - 将附加一个字符,其在 TeX 内部字符编码方案中的代码点编号等于\endlinechar读取和预处理该行时整数参数 current 的值。该参数通常具有值 13,因此表示回车符。甚至在标记化发生之前就删除了行右端的空格字符的情况意味着,使用非LuaTeX-TeX引擎/单独使用LuaTeX引擎的TeX前端,您无法以保留行尾空格的方式逐字读取/逐字复制内容。

\documentclass{article}

\usepackage{robust-externalize}

\robExtConfigure{
  enable fallback to manual mode, % avoid error if shell escape is forgotten/not used, print instead the command in the pdf
  new preset={my python exec}{
    python,
    custom include command={\mytmpvalue},
    add import={import math},
    set placeholder={__ROBEXT_MAIN_CONTENT__}{write_to_out(r"\gdef\mytmpvalue{" + str(__ROBEXT_MAIN_CONTENT_ORIG__)+ r"}")}
  },
}
%----------------------------------------------------------------------
\NewDocumentCommand\myOtherPython {} {%
  \begingroup
  % The fake-file-writing-part of `\scantokens` shall take returns for
  % directives to write another line:
  \newlinechar=\endlinechar
  % horizontal tab (ASCII 9, 9th letter of alphabet is I) has
  % category 10(space) even in verbatim-mode, so give it
  % category 12(other).
  \catcode`\^^I=12\relax
  \myOtherPythoninner
}
\NewDocumentCommand{\myOtherPythoninner}{+v+v}{%
  \RenewDocumentCommand{\myOtherPythoninner}{v}{%
     \scantokens{#1##1#2}%
  }%
}%
\myOtherPythoninner{\endgroup\begin{CacheMeCode}{my python exec}
}{
\end{CacheMeCode}
%}
% The percent with `%}` must be! It is read verbatim and goes verbatim
% into the final definition of \myOtherPythoninner. It is the last thing
% fed to \scantokens. \scantokens is like writing tokens unexpanded to file
% and then reading that file, hereby tokenizing things according to the current
% catcode-régime. Reading files is linewise, with pre-processing, i.e.
% removal of spaces at line ends and appending a character according
% to \endlinechar. The percent will be right before the endline-
% character inserted at the end of the last "line", in the stage of
% tokenizing causing TeX to skip that endline-character as a comment.
%----------------------------------------------------------------------
\NewDocumentCommand\myPython {} {%
  \begingroup
  % horizontal tab (ASCII 9, 9th letter of alphabet is I) has
  % category 10(space) even in verbatim-mode, so give it
  % category 12(other).
  \catcode`\^^I=12\relax
  \myPythoninner
}
\NewDocumentCommand{\myPythoninner}{v}{\endgroup\cacheMe[my python exec]{#1}}%
%----------------------------------------------------------------------

\begin{document}

On my system you see the result: !!\myOtherPython{math.sqrt(42 % 9)}!!

On my system you see the result: !!\myPython{math.sqrt(42 % 9)}!!

Compare with:
!!\begin{CacheMeCode}{my python exec}
math.sqrt(42 % 8)
\end{CacheMeCode}
!!
  
\end{document}

% Local Variables:
% TeX-command-extra-options: "--shell-escape -halt-on-error"
% End:

在此处输入图片描述




现在让我们看一下第二个示例,其中您尝试使用v-type-argument 如下:

Why is this printing the actual content instead of the result: \myPython{math.sqrt(42 % 9)}??

环境CacheMeCode与 环境 相同RobExtCacheMeCode,后者又调用环境 的起始命令RobExtPlaceholderFromCode。该命令又执行\XSIMfilewritestart\XSIMfilewritestart在 xsimverb.sty 中定义,因此它的文件写入机制通过使行尾符(回车符,字符代码 13)处于活动状态,并将活动回车符标记作为宏来工作,该宏作为其分隔参数将事物带到下一个活动回车符标记,并在处理其参数后重新插入作为参数分隔符移除的活动回车符标记。因此,\XSIMfilewritestart的文件写入机制在处理\begin{CacheMeCode}并将该行尾符标记为活动字符标记后,第一次遇到下一个行尾/下一个行尾时会影响事物。 和下一个行尾/下一个行尾之间的大部分内容\begin{CacheMeCode}都像往常一样被标记、处理和排版。

但是,当(如您的场景中一样)所有内容(包括\begin{CacheMeCode}环境主体和\end{CacheMeCode})都是扩展宏的结果\myPython时,则会在处理后遇到下一个换行符/下一个行尾字符之前遇到结束环境的命令。因此,由→ →\begin{CacheMeCode}初始化的文件写入机制在有机会开始运行之前就被取消初始化。因此,环境的整个主体(即的 v 类型参数)被排版到 .pdf 文件中,并且主体的任何内容都不会写入外部文本文件。\begin{CacheMeCode}\RobExtPlaceholderFromCode\XSIMfilewritestart\end{CacheMeCode}\myPython


现在让我们看看你的评论:

实际上,如果我使用\str_set:Nn \l_tmpa_str {#2},则新行将变成^^M。您知道为什么/如何避免这种情况吗?

根据 interface3.pdf,通过\str_set:Nn标记列表#2将其转换为字符串,并将转换结果存储在中\l_tmpa_str。转换由例程完成\tl_to_str:n,它只是原语的另一个名称\detokenize\detokenize就像应用于\string每个标记并将显式空格标记附加到控制字标记的字符串化和将类别 6(哈希)的显式字符标记的字符串化加倍一样。 (\string反过来提供类别 12(其他)的显式字符标记,唯一的例外是空格作为类别 10(空格)的显式字符标记提供。应用于\string⟨控制字标记⟩的结果受参数的影响\escapechar。)
字符到^^-notation 的转换既不是用\detokenize也不是用\scantokens。字符转换取决于计算机平台和使用的 TeX 引擎,并且只有当 TeX 确实将某些内容写入外部文本文件或屏幕/控制台/shell 时才会完成。

您可以使用完全允许的 TeX 引擎来避免使用回车符进行字符转换,该引擎完全不进行字符转换,或者至少不进行使用回车符的字符转换。在现代 TeX 平台上,TeX 二进制文件具有用于影响字符转换的命令行选项和配置文件 (.tcx 文件)。

在某种程度上,您可以通过同时提供\newlinechar\endlinechar表示回车符的值 13 以及提供回车符类别 12 来处理换行符。通过这些设置,在预处理阶段附加在 .tex 输入的每一行末尾的结束符在标记时会产生类别 12 的明确回车符标记。在写入时,这种类别 12 的明确回车符标记被视为指令,根据计算机平台的条件开始写入另一行文本,而不是将其写为字符,因此可能需要进行字符转换为^^-notation。


了解了何时进行字符翻译以及何时不进行字符翻译之后,让我们来看看:

编辑3事实上,如果我把字符串逐字逐句地放进去,这个问题并没有得到解决

\ExplSyntaxOn
\str_new:N \__robExt_tmp_contain_code_str
\NewDocumentCommand{\robExtCacheMeCode}{O{}+v}{%
  {% Group
    %% We store the input in a non-string element for efficiently implementing "auto forward"
    \str_set:Nn \__robExt_tmp_contain_code_str {#2}
    \str_show:N \__robExt_tmp_contain_code_str
}}

然后,如果输入包含换行符,它们实际上会被替换为 ^^M 并且我找不到如何用适当的换行符替换它们。

+v当整数参数设置为 13 时,将从 .tex 输入文件中读取并标记属于 -argument 的标记\endlinechar,在 TeX 的内部字符表示方案中,13 表示回车符,并且类别代码 12(其他)被分配给回车符。因此,在读取和标记属于 -argument 的标记时+v,换行符会产生类别 12(其他)的单个显式回车符标记,即字符代码 13 和类别 12(其他)的显式字符标记。应用\str_set:Nn不会对此类字符标记进行任何更改,因为它们已经属于类别 12。

但是,当\str_show:N将字符串变量的值写入 shell/screen/console/standard output 时,在写入过程中会发生字符转换,因此显式回车符标记将显示为^^M。因此,^^M您在 shell/screen/console/standard output 上看到的不是您的+v参数的显式回车符标记 / 您的参数的字符串化的+v回车符标记被替换为的东西。它们只是 TeX 引擎在 shell/screen/console/standard output 上显示回车符的方式,在写入时会发生字符转换。

如果您在代码中稍微更改一些内容,这样就不会显示整个字符串,而是显示字符串中每个字符的含义,您会看到^^M shell/屏幕/控制台/标准输出上显示的序列并不代表三个标记^^而是M代表一个标记,即显式回车符标记:

\ExplSyntaxOn
\str_new:N \__robExt_tmp_contain_code_str
\NewDocumentCommand{\robExtCacheMeCode}{O{}+v}{%
  {% Group
    \str_set:Nn \__robExt_tmp_contain_code_str {#2}
    \tex_message:D {^^J \str_map_function:NN \__robExt_tmp_contain_code_str \mystuff_meaningAndLinebreak }
}}
\cs_new:Npn \mystuff_meaningAndLinebreak #1 {\cs_meaning:N #1 ^^J}
\ExplSyntaxOff
\robExtCacheMeCode{z
Z}
\stop

在 shell/屏幕/控制台/标准输出上您会收到以下消息:


the character z
the character ^^M
the character Z
 

如果您在 shell/屏幕/控制台/标准输出上看到的序列^^M代表三个字符标记^, ^M而不是单个明确的回车符标记,那么消息将会有所不同:

而不是单行

the character ^^M

,其中^^M由于在写入 shell/屏幕/控制台/标准输出时进行字符转换,也代表单个回车符,因此您将有三行

the character ^
the character ^
the character M

相关内容