是否可以定义一个宏\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
可能就足够了。\myPython
v
\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]
v
v
为了通过模式分配为水平制表符分配类别代码 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
。