不能在 \edef 中使用 \textit 或 \url

不能在 \edef 中使用 \textit 或 \url

我正在尝试编写一个 Tex 命令,自动打印出我在文档中使用的图像的归属信息。我创建了 2 个不同的命令。第一个命令是\nounattr(带有 3 个参数),它接受图像的名称、作者和图像的链接。第二个命令是\printnounattrs没有参数,但应该只打印在使用之前添加的所有归属的列表\nounattr。我能够执行这些命令并获得一个简单的列表,但我无法向列表添加任何样式。

这是我目前所拥有的:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{nounattr}[2018/01/03 The noun project attribution]

\RequirePackage{svg}
\RequirePackage{url}

\newcommand\printnounattrs{}
\newcommand\nounattr[3]{\edef\printnounattrs{\printnounattrs#1 pictogram werd ontworpen door #2 van The Noun Project, onder de Creative Commons license (CC BY 3.0). Zie #3. }}

MWE 如下:

\documentclass{report}
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
\usepackage[parfill]{parskip}
\usepackage{nounattr}
\begin{document}
    \nounattr{Protein}{Ben Markoch}{https://thenounproject.com/search/?q=protein\&i=54837}
    \nounattr{Peptides}{Fredrik Edfors}{https://thenounproject.com/search/?q=peptide\&i=66903}

    \printnounattrs
\end{document}

得出的结果是: 在此处输入图片描述

不过,我想在 edef 中添加一些样式,以便以#1 pictogram斜体打印,并且使用 正确定义 url 为 url \url{#3}。我将其更改\nounattr为:

\newcommand\nounattr[3]{\edef\printnounattrs{\printnounattrs\textit{#1 pictogram} werd ontworpen door #2 van The Noun Project, onder de Creative Commons license (CC BY 3.0). Zie \url{#3}. }}

但这给了我一个undefined control sequence错误。我不明白发生了什么,我该如何解决这个问题?

答案1

有大量的 TeX 宏/命令无法(按预期)在\edef。这些宏称为不可扩展。TeX 编程的这个方面迟早会逐渐出现,如果想在 (La)TeX 编码方面更进一步,那么值得一读。这个网站上有很多关于此类问题的问题(例如\def、\edef、\gdef 和 \xdef 之间有什么区别?何时使用 \edef、\noexpand 和 \expandafter?, ...),但除了典型的 TeX 参考书(如 TeX 书或 TeX by Topic)之外,我不知道有什么好的系统介绍(例如参见学习 TeX 的最佳方法是什么?我应该读 Donald Knuth 的《The TeXbook》吗?)。

在您的特定情况下\textit,并且\url不可扩展,因此在使用时会中断\edef

如果你想保留当前方法的结构,你可以用\edefs\expandafter和 a替换\def

\documentclass{report}
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
\usepackage[parfill]{parskip}
\RequirePackage{url}

\newcommand\printnounattrs{}
\newcommand\nounattr[3]{%
  \expandafter\def\expandafter\printnounattrs\expandafter{%
    \printnounattrs
    \textit{#1 pictogram} werd ontworpen door #2 van The Noun Project,
    onder de Creative Commons license (CC BY 3.0).
    Zie \url{#3}. }%
}
\begin{document}
    \nounattr{Protein}{Ben Markoch}{https://thenounproject.com/search/?q=protein&i=54837}
    \nounattr{Peptides}{Fredrik Edfors}{https://thenounproject.com/search/?q=peptide&i=66903}
    \nounattr{Peptides}{Schmeptides}{https://thenounproject.com/search/?q=peptide&i=123&pq=v_w20w}

    \printnounattrs
\end{document}

虽然\edef会尝试尽可能地扩展其参数中的所有内容,但\expandafter\def\expandafter\printnounattrs\expandafter{\printnounattrs只会\printnounattrs在定义中扩展一次,这意味着我们可以按预期连接列表。(简单的\def\printnounattrs{\printnounattrs...}会导致无限循环,因为这将定义\printnounattrs要扩展为的宏\printnounattrs。)

请注意,这种方法\url存在常见的问题,即 URL 不能包含%#或者^^以 结尾,\因为它被用作命令的参数。


如果我要从头开始编写一个用于类似任务的宏,我可能会考虑使用etoolbox或 LaTeX3/的强大列表宏expl3

以下是一个简单的实现etoolbox

\documentclass{report}
\usepackage{etoolbox}
\usepackage{url}

\newcommand\mylist{}
\newcommand\nounattr[3]{%
  \listadd{\mylist}{{#1}{#2}{#3}}}

\newcommand*{\printnounattr}[3]{%
  \textit{#1 pictogram} werd ontworpen door #2 van The Noun Project,
  onder de Creative Commons license (CC BY 3.0).
  Zie \url{#3}.
}

\makeatletter
\newcommand*{\printnounattr@i}[1]{%
  \printnounattr#1}
\newcommand*{\printnounattrs}{%
  \forlistloop{\printnounattr@i}{\mylist}}
\makeatother

\begin{document}
    \nounattr{Protein}{Ben Markoch}{https://thenounproject.com/search/?q=protein&i=54837}
    \nounattr{Peptides}{Fredrik Edfors}{https://thenounproject.com/search/?q=peptide&i=66903}
    \nounattr{Peptides}{Schmeptides}{https://thenounproject.com/search/?q=peptide&i=123&pq=v_w20w}

    \printnounattrs
\end{document}

答案2

\edef将所有命令完全展开。但是,并非所有命令都可以展开,例如 eg,\textit因为当它不处于“打印模式”时,它不知道如何处理文本。如果您仍然想展开其他标记,而 eg 保持\textit不变,则可以使用\noexpand(或\unexpanded多个标记)来阻止它展开(而所有其他标记都展开):

\documentclass{article}
\def\a{alpha}
\edef\b{\noexpand\textit{\a}}
\show\b
\begin{document}
\b
\end{document}

在日志中显示这一点:

> \b=macro:
->\textit {alpha}.
l.4 \show\b

您可以在以下网址阅读更多相关信息\noexpand(及相关内容\unexpanded这个答案

相关内容