转载:LaTeX 定义参数变长的命令

2023-05-16

本文作者: Liam Huang
本文链接: https://liam.page/2017/07/30/define-a-new-command-with-different-amount-of-parameters-in-LaTeX/
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

在 C++ 中,我们可以为同一个函数赋予不同的执行内容,这种行为称之为「函数重载」。具体重载的函数,共享同一个函数名,但是接收的函数参数在数量、类型上不同。LaTeX 是宏语言,没有一般意义上的参数类型的说法。但是,有没有办法在 LaTeX 中「重载」一个宏,根据输入的参数数量不同,而产生不同的效果呢?

本文给出解决方案。

在 TeX 和 LaTeX2e 中定义新命令

TeX 中,定义新命令的标准方法是使用 TeX 原语 \def。它有几个变种,记录如下。

  • \def:局部定义,定义时不展开;
  • \edef:局部定义,定义时完全展开;
  • \gdef:相当于 \global\def;
  • \xdef:相当于 \global\edef。

建立在 TeX 之上的各种格式,其提供的定义新命令的方案,都是通过这四个 \def 来实现的。LaTeX2e 中定义新命令的标准方法是使用 \newcommand。它也有几个变种,记录如下。

  • \newcommand:新定义一个命令,如果该命令已有定义,则报错;
  • \renewcommand:重定义一个命令,如果该命令未定义,则报错;
  • \providecommand:如果该命令未定义,则定义一个新的命令;否则,啥也不干。

当然,在 LaTeX2e 中,也有 \DeclareRobustCommand 一系列命令,可以用来定义新的命令。这一系列命令,是 LaTeX2e 针对「脆弱命令」问题,提供的一些保护机制。此处不表。

在标准的方法中,不论是 TeX 还是 LaTeX2e,都没有提供「参数变长」的实现方法。也就是说,如果不引入奇怪的技巧,我们在普通的 LaTeX 文稿中,是无法重载命令的。

\@ifnextchar

\@ifnextchar 是一个 LaTeX 内部宏。它的作用,是「预读」输入列表的下一个字符,然后判断预读的字符是否与作者期待的字符一致,执行不同的分支。

例如,我们知道,LaTeX 命令的可选参数,默认是放在所有必选参数之前。那么,我们是否有可能让可选参数放在必选参数之后呢?答案当然是肯定的,利用 \@ifnextchar 就可以做到。

\documentclass{article}
\makeatletter
\newcommand{\foo@helper@i}[1]{One parameter: #1{}.}
\def\foo@helper@ii #1[#2]{Two parameters: #1{}, #2{}.}
\newcommand{\foo}[1]{%
\@ifnextchar[%
  {\foo@helper@ii{#1}}%
  {\foo@helper@i{#1}}%
}
\makeatother
\begin{document}
\foo{hello}

\foo{hello}[world]
\end{document}

我们来看 \foo 的定义。它接收一个标准的 LaTeX 参数。因此不管是 \foo{hello} 还是 \foo{hello}[world],LaTeX 都会把其中的 \foo{hello} 先「吃下去」。接下来,LaTeX 会判断下一个字符是否为 [。对于 \foo{hello} 这种用法,下一个字符是换行符,因此判定失败,执行 \foo@helper@i。而对于 \foo{hello}[world] 这种用法,吃下去 \foo{hello} 之后,输入流中剩下了 [world]...,下一个字符正是 [,因此执行 \foo@helper@ii

对于 \foo@helper@ii,它是使用 TeX 的原语 \def 定义的命令。参数列表 #1[#2] 表示该命令接受两个参数。第一个参数是标准的 TeX 参数——用分组包括起来。因此,上一步执行的 \foo@helper@ii 将第一个参数喂给了 \foo@helper@ii。接下来,\foo@helper@ii 还要吃下去第二个参数。按照定义,第二个参数被方括号 [] 所包围。因此 [world] 中的 world 被吃掉,作为第二个参数。

最终输出如图。(省了)

\bgroup

上面的 \foo 命令,基本已经达成了我们的目标。只不过,第二个参数必须是用方括号表达的。当然这不是不可以,但强迫症选手们可能会希望第二个参数也能用花括号来界定。强迫症们尝试把 \@ifnextchar[ 尝试换成了 \@ifnextchar{,于是他们得到了报错

File ended while scanning use of...

这是因为,TeX 遇到 { 时,会将其解释为一个分组。因此,这种写法会造成 TeX 读入的分组不匹配。这样一来,我们就必须用 \bgroup 来代替花括号。它的定义是 \let\bgroup={

\documentclass{article}
\makeatletter
\newcommand{\foo@helper@i}[1]{One parameter: #1{}.}
\newcommand{\foo@helper@ii}[2]{Two parameters: #1{}, #2{}.}
\newcommand{\foo}[1]{%
\@ifnextchar\bgroup%
  {\foo@helper@ii{#1}}%
  {\foo@helper@i{#1}}%
}
\makeatother
\begin{document}
\foo{hello}

\foo{hello}{world}
\end{document}

这样一来,我们就实现了一个 \foo 命令,在参数不同的情况下,具有不同的行为。

xparse 宏包

基于 LaTeX3 的 xparse 宏包给了我们新的选项。它提供的 \NewDocumentCommand 命令,允许用户使用新的接口定义 LaTeX 命令。其形式为

\NewDocumentCommand{<command>}{<parameter specificers>}{<replacement text>}

比如,以下两个定义,效果是一致的。

\usepackage{xparse}

\newcommand{\baz}[1]{I eat #1{}.}
\NewDocumentCommand{\bar}{m}{I eat #1{}.}

其中,参数标识符 m 表示 \bar 接收一个标准的 LaTeX 参数。除去 m 之外,xparse 宏包还提供了许多额外的参数标识符(具体参照其手册)。其中,g 表示该参数是一个可选参数,并且以花括号界定其范围。当参数未给出时,参数值为 -NoValue-;否则是实际的参数内容。此时我们可以用 \IfNoValueTF 命令来做分支判断。

于是,上述 \foo 命令可以按如下方式实现。

\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\foo}{mg}{%
  \IfNoValueTF{#2}%
  {One parameter: #1{}.}%
  {Two parameters: #1{}, #2{}.}%
}
\begin{document}
\foo{hello}

\foo{hello}{world}
\end{document}

这样的实现方式,相对在 LaTeX2e 里用 \@ifnextchar\bgroup 判断就简单清晰多了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

转载:LaTeX 定义参数变长的命令 的相关文章

  • Redis基本操作

    安装Redis docker compose安装 单节点 version 34 3 3 34 services master image redis 6 0 container name redis environment 34 disco
  • RabbitMQ初级操作

    MQ 消息队列 xff1a 一种进程通信或同一进程的不同线程的通信方式 采用链表结构实现 xff0c 拥有权限的进程向消息队列写入或读取消息 常用MQ xff1a RabbitMQ ActiveMQ kafka xff0c Redis的Li
  • SpringBoot——邮件模块

    SpringBoot集成邮件模块 邮件协议 原文链接 xff1a https blog csdn net qq 37745470 article details 89094227 什么是SMTP xff1f SMTP全称为Simple Ma
  • JVM内存结构

    JVM JVM xff1a Java Virtual Machine xff0c Java虚拟机 xff0c 识别 class后缀文件 xff0c 解析class的指令 xff0c 调用操作系统函数完成操作 JVM JRE JDK关系 xf
  • JVM类加载机制

    类加载过程3个阶段 xff1a 1 加载 将类的字节码载入方法区 xff0c 并创建 Class对象 加载符号引用到类常量池 xff0c 只是有这个名字UnresolvedClass Name 如果类的父类没有加载 xff0c 先加载父类加
  • nginx 反向代理配置

    Nginx 反向代理 xff1a 其实客户端对代理是无感知的 xff0c 因为客户端不需要任何配置就可以访问 xff0c 我们只需要将请求发送到反向代理服务器 xff0c 由反向代理服务器去选择目标服务器获取数据后 xff0c 在返回给客户
  • Cookies 和 Session的区别

    1 cookie 是一种发送到 客户 浏览器的文本串句柄 xff0c 并保存在客户机硬盘上 xff0c 可以用来在某个WEB站点会话间持久的保持数据 2 session 其实指的就是访问者从到达某个特定主页到离开为止的那段时间 Sessio
  • HashMap详解

    HashMap 16 xff0c 0 75 为什么能快速查找 xff1f put操作放入key时 获取key的hash值 在原始hash值的基础上再次获取hash值 二次hash值与容量取模运算 capacity 获得桶下标 根据桶下标 x
  • 设计模式——单例模式

    单例模式 饿汉式 类加载阶段被初始化就会创建实例 提前创建 span class token keyword class span span class token class name Singleton span span class
  • Java多线程

    并发 线程状态 Debug调试 xff0c 线程模式 java xff1a 6种状态 NEW 新建 startRUNNABLE 就绪 运行 阻塞I O cpu 调度TERMINATED 终结 代码执行完毕BLOCKED 阻塞 获取锁失败WA
  • JVM垃圾回收

    GC与分代回收算法 GC目的 xff1a 实现无用对内存自动释放 xff0c 减少内存碎片 加快分配速度 GC要点 xff1a 回收区域 xff1a 堆内存可达性分析算法 三色标记法GC具体实现称垃圾回收器GC采用分代回收思想 xff0c
  • 4种对象引用类型

    1 强引用 A a 61 new A 通过GC Root的引用链 xff0c 如果强引用不到该对象 xff0c 该对象才会被回收 2 软引用 SoftReference a 61 new SoftReference new A 如果仅有软引
  • SQL语句

    Select SQL 执行顺序 fromonjoinwheregroup byhavingselectdistinctorder bylimit WHERE 字段比较 代码作用 61 等于 lt gt 61 不等于 lt lt 61 小于
  • Bootstrap笔记

    Bootstrap样式 CSS导入 span class token tag span class token tag span class token punctuation lt span link span span class to
  • JSTL与EL表达式

    什么是JSTL JSTL是对EL表达式的扩展 xff0c JSTL是标签语言 xff01 规范了每个标签的职责范围 JSTL标签库 core 核心标签库 fmt 格式化标签库 导标签包 span class token operator l
  • Ubuntu中/usr/local 和 ~/.local 之间的区别

    Ubuntu中 usr local 和 local 之间的区别 usr local 是一个可供所有用户使用的软件可由管理员安装的地方 local bin 是一个用户可以安装软件供自己使用的地方 不同发行版和社区使用的目录结构的历史有些混乱
  • Centos7配置yum镜像源(base,extras,updates,epel,local)

    一 备份默认源 由于默认源都在国外 xff0c 速度非常慢 xff0c 需要把默认的源配置文件备份后删除 span class token comment 进入配置文件目录 span span class token function cd
  • win10彻底关闭windows update 自动更新的方法

    转载自 xff1a https jingyan baidu com article 6181c3e0d75aaa152ef15326 html 其实保留更新还是很有用的 xff0c 毕竟官方一直在修复漏洞 但是服务器虚拟机中运行的win10
  • 解决Centos 7 VNC黑屏

    在配置Centos 7下VNC时发现root用户可以正常登陆VNC桌面 xff0c 而普通用户VNC桌面黑屏 xff0c 分析 vnc xstarup 后发现是普通用户没有执行 etc X11 xinit xinitrc的权限 bin sh
  • 一个与cni0相关的pod创建问题

    今天查看k8s xff0c 发现有个coredns的pod创建失败 xff0c 查看这个POD的信息 xff0c 显示如下错误 combined from similar events Failed to create pod sandbo

随机推荐

  • Debian10安装SSH、配置NTP、安装配置UFW防火墙、配置PATH

    一 SSH安装配置 1 1 安装SSH span class token comment 安装SSH客户端 span apt span class token function install span openssh client spa
  • Debian10 创建用户、用户组、切换用户

    span class token comment 新建用户组 span span class token function groupadd span hausers span class token comment 新建用户并加入用户组
  • C++关于循环依赖的问题

    C 43 43 关于循环依赖的问题 xff1a 循环情况 xff1a class B class A public B b class B public A a 若两个类之间存在循环依赖则在编译时会报错 xff0c 原因是两个类中存在相互的
  • Rust小项目一:Rust 网络编程,实现一个Tcp server

    近日学习Substrate的开发入门 xff0c 之前没有接触过Rust编程 xff0c 今天跟着视频做个小项目练练手 项目目标 xff1a 编写一个Tcp server端与一个Tcp client端 xff0c 客户端中输入内容后 xff
  • Python 项目打包并发布到私有 PyPI 服务器

    推广博客 xff1a Python 项目打包并发布到私有 PyPI 服务器
  • C++ 零碎特性

    摘自 C 43 43 17 入门经典 几乎不会再更新 文章目录 使用花括号初始化变量零初始化使大整型字面量更加易读二进制的整型字面量 96 size t 96 类型浮点数的特殊情况 xff1a NaN xff08 Not a Number
  • Python 学习笔记——进阶

    文章目录 一 模块 xff08 一 xff09 1 导入外部模块2 导入时重命名3 标准库 xff08 一 xff09 96 sys 96 96 argv 96 变量 96 exit 96 函数 96 modules 96 变量 96 pa
  • C++ UTF-8 编码与 UTF-32 编码的互相转换

    C 43 43 UTF 8 编码与 UTF 32 编码的互相转换 代码实现基本照搬了秦建辉的博客 这里不介绍原理 xff0c 只提供可以直接使用的代码 要求 C 43 43 编译器的语言标准至少为 C 43 43 17 如果编译器支持的语言
  • C++ 默认移动构造函数的调用情况

    C 43 43 默认移动构造函数的调用 直接上测试代码 xff1a include lt cstdio gt include lt iostream gt include lt string gt class MyClass int a 6
  • LaTeX 003:使用 MikTeX 组件实现 pdf 转 eps

    气死了气死了 xff0c 这玩意儿居然直接百度不到一个很好的答案 xff0c 百度到的全是用带图形化界面的软件 xff0c 您不累吗 xff1f 唯一找到的一个 xff0c 效果不好 xff0c 是糊的 最后还是上谷歌镜像站 xff0c 一
  • 【深入浅出ios开发】UIStoryboardSegue详解

    一个UIStoryboardSegue对象负责执行两个试图控制器之间的视觉过渡 另外 xff0c segue对象通常用来准备从一个控制器过渡到另一个控制器 segue对象包含了涉及过渡的控制器的信息 当segue被触发 xff0c 并且在视
  • C++ 多线程编程导论(上)

    随着摩尔定律逼近失效和多核处理器快速发展 xff0c 多线程编程变得越来越重要 本文将系统介绍在 C 43 43 中如何使用 STL 实现多线程编程 多线程编程博大精深 xff0c 本文并不介绍多线程算法或多线程编程方法 xff0c 而是把
  • 使用 WaitForSingleObject 等待进程结束错误代码 5(拒绝访问)问题的解决

    说明使用 OpenProcess 的权限不够 xff0c 但是哪个权限呢 xff1f 查阅文档发现是 SYNCHRONIZE SYNCHRONIZE 0x00100000L 使用对象的同步机制的权限 该权限允许线程等待该对象 xff0c 直
  • LaTeX 004:隐藏 hyperref 超链接的红框

    针不戳 xff0c 很快找到答案了针不戳 以往的方法是设置超链接红框的颜色 hypersetup pdfauthor 61 UnnamedOrange colorlinks 61 true linkcolor 61 black anchor
  • LaTeX 005:删除一个命令

    C 43 43 的预编译系统中 xff0c 可以使用 undef 来取消 xff08 Undefine xff09 一个宏 LaTeX LaTeX L A T E X 中是否有类似于这样的功能呢 xff1f 网上的一个评论给出了解决方案 x
  • LaTeX 006:添加一个只有文字没有标号的脚标

    想要如下的效果 xff1a 该脚标只想要写一句注释 xff0c 没有对应的正文文本 如何实现 xff1f 直接使用 footnotetext 是不佳的 xff0c 前面会加上当前的脚标标号 xff0c 而且该标号不会自动递增 虽然 foot
  • GetExitCodeProcess 所需运行时间

    GetExitCodeProcess 在 Windows 中用于获取进程的返回代码 这个看上去只有一个读操作的函数运行速度如何呢 xff1f 直觉上不会很快 xff0c 因为它肯定涉及操作系统进程表的操作 下面做了一个实验 代码 xff1a
  • C++ 继承时返回值或参数的类型是父类的行为

    见以下代码的 base t amp operator 61 const base t amp another 还没有搞清楚 span class token macro property span class token directive
  • LaTeX 007:texify 调用 zhmakeindex

    如文档所述 xff0c 在系统增加一个值为 zhmakeindex 路径的环境变量 MAKEINDEX 即可
  • 转载:LaTeX 定义参数变长的命令

    本文作者 xff1a Liam Huang 本文链接 xff1a https liam page 2017 07 30 define a new command with different amount of parameters in