我有一个很大的宏文件,它们都是这种形式:
\def\eclaire{\mathbb}
\def\R{\ensuremath{\eclaire R}}
我想以\newcommand
表格形式拥有它们,对于那些宏来说,这很容易,我只需要\def
像\newcommand
这样替换:
\newcommand \eclaire{\mathbb}
\newcommand \R{\ensuremath{\eclaire R}}
但对于其他一些宏来说,例如这个:
\def\rond#1{\build#1_{}^{\>\circ}}
我不能,因为解决方案是:
\newcommand\rond[1]{\build#1_{}^{\>\circ}}
所以我的问题是,是否有一个脚本可以自动将我的\def
命令转换为\newcommand
?
预先感谢您的帮助。
答案1
\def
并且\newcommand
并不完全等同,因为\newcommand
如果要求重新定义现有命令,则会抛出错误。\newcommand
因此,如果您想定义新命令,则首选它,但它不能用于重新定义现有命令。这不是脚本可以轻松检测到的东西,所以我现在将忽略它,即使它与您的预期用例相关,例如,您有
\newenvironment{Cases}{%
\left\lbrace
\def\arraystretch{1.2}%
\array{@{}l@{\quad}l@{}}
}{%
\endarray\right.%
}
此外,\def
和\newcommand
不支持完全相同的语法定义。\newcommand
只能定义最多九个(强制)参数的宏,其中第一个参数可以是可选的(并且用方括号括起来)。\def
还可以支持其他分隔宏。我假设您的s 仅采用或的\def
形式。这已经由 提到过\def\<cmd>{...}
\def\<cmd>#1...#n
焦耳伏特 在评论中. 这个问题
\def\build#1_#2^#3{\mathrel{\mathop{\kern 0pt#1}\limits_{#2}^{#3}}} %CLENET
\newcommand
也不允许使用像\long
、\outer
、这样的前缀\protected
。因此,如果您使用这些前缀,您也无法轻松切换到\newcommand
。(除了可能映射到/的\long
/not 。)我假设您不使用任何这些前缀,我将使用,这使得它的参数,但您当然可以使用来获取“un ”参数。\long
\newcommand
\newcommand*
\newcommand
\long
\newcommand*
\long
如果我们允许这些简化
s/\\def(\\[^{#]*)(?:#+([0-9]))*{/\\newcommand{$1}[0$2]{/g
产生可接受的结果。
以下(粗略近似)Lua 脚本执行的操作非常类似。我没能在一个组中完成参数结构#1
、 、 ... 的匹配,因此我选择使用循环来完成此操作。#1#2
local io = require('io')
local string = string
local filename = arg[1] or nil
function newcommandify(content)
content = string.gsub(content, "\\def(\\[^{#]*){",
"\\newcommand{%1}{")
local iargs = ''
for i=1,9 do
iargs = iargs .. "#+" .. i
content = string.gsub(content, "\\def(\\[^{#]*)" .. iargs .. "{",
"\\newcommand{%1}[".. i .. "]{")
end
return content
end
if filename then
local file_handle = assert(io.open(filename, 'r'))
local content = file_handle:read('*all')
io.close(file_handle)
content = newcommandify(content)
file_handle = assert(io.open(filename .. "-newcommand", 'w'))
file_handle:write(content)
io.close(file_handle)
end
另存为def-to-newcommand.lua
并运行
texlua def-to-newcommand.lua <yourfilename>
<yourfilename>-newcommand
将\def
s 转换为得到\newcommand
。
答案2
为什么您希望将 方面的作业转换\def
为 方面的作业\newcommand
?
如果只是为了确保已经定义的宏不会被覆盖,那么可以采用另一种方法:
基本\newcommand
行为如下:
它检查星号和可选参数,并应用内核宏\@ifdefinable
来查明所讨论的控制序列标记是否已在当前范围内定义。
如果是这样,\@ifdefinable
将抛出一个错误消息并且不会(重新)定义有问题的控制序列标记。
如果不是这样,则所讨论的控制序列标记将根据\def
(,可能前面是\long
)来定义。在处理可选参数的情况下,用于检测可选参数是否存在的例程\newcommand
也将包含在由 执行的定义中。
注意事项\newcommand
:
\newcommand
本身就是一个宏/是基于宏的“机制”。如果宏的参数包含根据 定义的标记\outer
,则 (La)TeX 将发出错误消息。因此:如果您尝试通过 定义\newcommand
已经根据 定义的命令\outer
,您将不会收到有关已定义控制序列的错误消息,但您会收到一条错误消息! Forbidden control sequence found while scanning use of \new@command.
\newcommand
将检查命令是否已在当前范围内定义。如果没有,它将仅在当前范围内定义该命令。因此,当打算全局定义宏时,例如通过 ,检查\newcommand
并不完全安全\newcommand\foo{bar}\global\let\foo=\foo
。实际上,
\newcommand
“机制”并不检查命令是否已经定义,而是检查命令是否已经定义为原语含义以外的其他内容\relax
。它通过应用于\csname..\endcsname
命令的名称(通过应用\string
和删除前导反斜杠形成)然后\ifx
检查命令的含义是否等于\relax
-primitive 的含义来实现这一点。它这样做是因为\csname..\endcsname
它本身(无论 -parameter 的值如何\globaldefs
)在当前范围内(仅)将 -primitive 的含义分配\relax
给它形成的标记,以防它们在形成时未定义。因此,\newcommand
默默地重新定义已经定义且含义等于 -primitive 的控制序列标记\relax
。例如,您可以这样做\let\MyCommand=\relax % now \MyCommand is defined and its meaning equals the meaning of the \relax-primitive \newcommand\MyCommand{This is the redefinition of MyCommand which gets carried out without warnings/error-messages.}
您还可以在本地范围/组内执行此操作,以导致该范围内未定义
\newcommand
。\MyCommand
需要注意的是:如果你做了类似的事情
\begingroup \let\MyCommand=\relax % now \MyCommand is defined and its meaning equals the meaning of the \relax-primitive \newcommand\MyCommand{This is the redefinition of MyCommand which gets carried out without warnings/error-messages.} \global\let\MyCommand=\MyCommand \endgroup
,
\MyCommand
可能已经在组外定义,并且它将在组外/所有范围内被覆盖,而无需任何通知。
我建议的方法是:
方面的任务\def
总是有模式的
⟨prefix⟩\def⟨control sequence token⟩⟨parameter text⟩{⟨replacement text⟩}
, 和⟨字首⟩\global
=空虚或和/或\long
和/或的组合\outer
。
(使用\gdef
或\edef
或,\xdef
图案看起来会相应。)
你可以实现一个宏\definedchecker
,通过捕获左括号分隔的参数(→参见 TeXbook 中的#1#{
-notation)来收集⟨字首⟩,那个\def
,那个⟨控制序列标记⟩和⟨参数文本⟩然后检查它是否包含\def
或\gdef
或\edef
或\xdef
,如果是,则使用该信息拆分⟨字首⟩,那个\def
,那个⟨控制序列标记⟩和⟨参数文本⟩在应用之前将其分开\@ifdefinable
- 但请注意,\@ifdefinable
当不仅在当前范围内而且在全局范围内定义控制序列标记时,-check 并不完全保存。
换句话说:您可以实现一个宏\definedchecker
,将其添加到您的\def
/ \gdef
/ \edef
/\xdef
序列中,并检查所讨论的控制序列标记是否已定义在当前范围内如果是,则传递错误消息,如果不是,则执行分配。
!!!请注意,当进行全局定义时,检查当前范围内定义的控制序列标记并不完全安全!!!
% Compile this example by calling LaTeX from a shell/command-prompt/console
% where you can also see the messages of the program!
%
\documentclass{article}
\makeatletter
%%<-------------------------------------------------------------------->
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\expandafter\expandafter\@firstoftwo{ }{}%
\@secondoftwo}{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
}%
%%<-------------------------------------------------------------------->
%% Code for definedchecker
%%......................................................................
\@ifdefinable\UD@gobbleto@def{\long\def\UD@gobbleto@def#1\def{}}%
\@ifdefinable\UD@gobbleto@gdef{\long\def\UD@gobbleto@gdef#1\gdef{}}%
\@ifdefinable\UD@gobbleto@edef{\long\def\UD@gobbleto@edef#1\edef{}}%
\@ifdefinable\UD@gobbleto@xdef{\long\def\UD@gobbleto@xdef#1\xdef{}}%
\newcommand\UD@CheckWhetherNoDef[1]{\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbleto@def#1\def}}%
\newcommand\UD@CheckWhetherNoGdef[1]{\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbleto@gdef#1\gdef}}%
\newcommand\UD@CheckWhetherNoEdef[1]{\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbleto@edef#1\edef}}%
\newcommand\UD@CheckWhetherNoXdef[1]{\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbleto@xdef#1\xdef}}%
\@ifdefinable\UD@catchto@gef{\long\def\UD@catch@def#1\def#2#3#{\innerdefinedchecker{#1}{\def}{#2}{#3}}}%
\@ifdefinable\UD@catchto@gdef{\long\def\UD@catch@gdef#1\gdef#2#3#{\innerdefinedchecker{#1}{\gdef}{#2}{#3}}}%
\@ifdefinable\UD@catchto@edef{\long\def\UD@catch@edef#1\edef#2#3#{\innerdefinedchecker{#1}{\edef}{#2}{#3}}}%
\@ifdefinable\UD@catchto@xdef{\long\def\UD@catch@xdef#1\xdef#2#3#{\innerdefinedchecker{#1}{\xdef}{#2}{#3}}}%
\newcommand\innerdefinedchecker[5]{\@ifdefinable{#3}{#1#2#3#4{#5}}}%
\@ifdefinable\definedchecker{%
\long\def\definedchecker#1#{%
\UD@CheckWhetherNoDef{#1}{%
\UD@CheckWhetherNoGdef{#1}{%
\UD@CheckWhetherNoEdef{#1}{%
\UD@CheckWhetherNoXdef{#1}{%
#1%
}{\UD@catch@xdef#1}%
}{\UD@catch@edef#1}%
}{\UD@catch@gdef#1}%
}{\UD@catch@def#1}%
}%
}%
\makeatother
\definedchecker\xdef\eclaire{\noexpand\mathbb}
\show\eclaire
\definedchecker\def\R{\ensuremath{\eclaire R}}
\show\R
\definedchecker\outer\global\long\def\myWeirdCommand#1Delimier#2delimiter#{This is my weird command}
\show\myWeirdCommand
% These throw "command already defined"-errors and leave the previous meanings untouched:
\definedchecker\edef\eclaire{Weird Redefinition of eclaire}
\show\eclaire
\definedchecker\gdef\R{Weird Redefinition of R}
\show\R
% Don't do this as \myWeirdCommand is outer.
% \newcommand\myWeirdCommand{Blah}
\begin{document}
\end{document}
以下是我收到的信息测试日志将其另存为测试.tex并使用 pdflatex 进行编译:
> \eclaire=macro:
->\mathbb .
l.111 \show\eclaire
?
> \R=macro:
->\ensuremath {\eclaire R}.
l.114 \show\R
?
> \myWeirdCommand=\long\outer macro:
#1Delimier#2delimiter{->This is my weird command{.
l.117 \show\myWeirdCommand
?
! LaTeX Error: Command \eclaire already defined.
Or name \end... illegal, see p.192 of the manual.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.121 ...ef\eclaire{Weird Redefinition of eclaire}
?
> \eclaire=macro:
->\mathbb .
l.122 \show\eclaire
?
! LaTeX Error: Command \R already defined.
Or name \end... illegal, see p.192 of the manual.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.124 ...edchecker\gdef\R{Weird Redefinition of R}
?
> \R=macro:
->\ensuremath {\eclaire R}.
l.125 \show\R
从这些消息中你可以看到:
如果当前范围内的相关命令已被定义为具有 -primitive 之外的含义\relax
,\definedchecker
则会产生错误并且不会执行分配。
否则将执行分配。
请注意,关于避免覆盖已定义的宏的定义,当涉及到全局定义宏时,此策略并不完全安全。
答案3
Python 程序def_to_newcommand.py
:
#!/usr/bin/env python3
import argparse
import re
def main():
args = parse_command_line()
data = read(args.input)
data = convert(data)
write(args.output, data)
def parse_command_line():
parser = argparse.ArgumentParser(
description='Replace \\def with \\newcommand where possible.',
)
parser.add_argument(
'input',
help='TeX input file with \\def',
)
parser.add_argument(
'--output',
'-o',
required=True,
help='TeX output file with \\newcommand',
)
return parser.parse_args()
def read(path):
with open(path, mode='rb') as handle:
return handle.read()
def convert(data):
return re.sub(
rb'((?:\\(?:expandafter|global|long|outer|protected)'
rb'(?: +|\r?\n *)?)*)?'
rb'\\def *(\\[a-zA-Z]+) *(?:#+([0-9]))*\{',
replace,
data,
)
def replace(match):
prefix = match.group(1)
if (
prefix is not None and
(
b'expandafter' in prefix or
b'global' in prefix or
b'outer' in prefix or
b'protected' in prefix
)
):
return match.group(0)
result = rb'\newcommand'
if prefix is None or b'long' not in prefix:
result += b'*'
result += b'{' + match.group(2) + b'}'
if match.lastindex == 3:
result += b'[' + match.group(3) + b']'
result += b'{'
return result
def write(path, data):
with open(path, mode='wb') as handle:
handle.write(data)
print('=> File written: {0}'.format(path))
if __name__ == '__main__':
main()
使用示例:
python3 def_to_newcommand.py foo.tex --output foo_changed.tex
以下定义
\def\eclaire{\mathbb}
\def\R{\ensuremath{\eclaire R}}
\def\rond#1{\build#1_{}^{\>\circ}}
\def\build#1#2#3{\mathrel{\mathop{\kern 0pt#1}\limits_{#2}^{#3}}}
\def\build#1_#2^#3{\mathrel{\mathop{\kern 0pt#1}\limits_{#2}^{#3}}}
\long\def\eclaire{\mathbb}
\long\def\R{\ensuremath{\eclaire R}}
\long\def\rond#1{\build#1_{}^{\>\circ}}
\long\def\build#1#2#3{\mathrel{\mathop{\kern 0pt#1}\limits_{#2}^{#3}}}
\long\def\build#1_#2^#3{\mathrel{\mathop{\kern 0pt#1}\limits_{#2}^{#3}}}
\global\long\def\eclaire{\mathbb}
\global\def\eclaire{\mathbb}
\protected\def\eclaire{\mathbb}
\long
\def \eclaire {\mathbb}
转换为:
\newcommand*{\eclaire}{\mathbb}
\newcommand*{\R}{\ensuremath{\eclaire R}}
\newcommand*{\rond}[1]{\build#1_{}^{\>\circ}}
\newcommand*{\build}[3]{\mathrel{\mathop{\kern 0pt#1}\limits_{#2}^{#3}}}
\def\build#1_#2^#3{\mathrel{\mathop{\kern 0pt#1}\limits_{#2}^{#3}}}
\newcommand{\eclaire}{\mathbb}
\newcommand{\R}{\ensuremath{\eclaire R}}
\newcommand{\rond}[1]{\build#1_{}^{\>\circ}}
\newcommand{\build}[3]{\mathrel{\mathop{\kern 0pt#1}\limits_{#2}^{#3}}}
\long\def\build#1_#2^#3{\mathrel{\mathop{\kern 0pt#1}\limits_{#2}^{#3}}}
\global\long\def\eclaire{\mathbb}
\global\def\eclaire{\mathbb}
\protected\def\eclaire{\mathbb}
\newcommand{\eclaire}{\mathbb}
评论:
\def
映射到\newcommand*
并且\long\def
映射到\newcommand
。如果可以检测到
\global
、\protected
或\outer
,则定义保持不变。仅支持空参数文本(
\def\foo{...}
)或未分隔的参数( )。不支持任意参数文本()和分隔参数()。\def\foo#1#2#3#4#5#6#7#8#9{...}
\newcommand
\def\foo bar{...}
\def\foo[#1]{...}
对命令名称后的空格有一定的支持(
\def \foo {...}
)。如果
\expandafter
检测到,则定义保持不变。(例如\expandafter\expandafter\expandafter\def\generatecmd{foo}{...}
:)当然,
\newcommand
如果命令已经定义,则会抛出错误。这需要手动干预,因为存在不同的解决方案:- 可以更改命令名称以避免名称冲突。
- 如果覆盖是故意的,
\renewcommand
则可解决问题。