将代码(列表、逐字或其他方法)包装在新命令中

将代码(列表、逐字或其他方法)包装在新命令中

对于我从事的一些教学工作,我希望同时使用两种不同语言的代码。为此,我拼凑了一个newcommand使用tabular环境来安排两个verb环境的程序,但问题很多:

  • 缩进只能通过将空格更改为 ~ 来实现

  • 任何数学符号都必须用 进行转义\,这就需要知道哪些要转义,哪些不要转义(例如,不能转义 <,否则会抱怨)

  • 赋值运算符在编译时<-会被颠倒过来。!-

环境listings也不起作用。错误消息是:

Runaway argument?
! Forbidden control sequence found while scanning use of \lst@next.
<inserted text> 
                \par 
l.23 \codeListing{x <- y}{f(g(x))}

以下是重现该问题的一些代码:

\documentclass[english]{article}
\usepackage[latin9]{inputenc}
\usepackage{geometry}
\begin{document}

\newcommand{\codeListing}[2]{
\begin{tabular}{p{8cm} p{8cm}}
\begin{lstlisting}
#1
\end{lstlisting}
&
#2\tabularnewline
\end{tabular}
}

\section{verbatim test}
\begin{verbatim}
logLik <- function(x) { 
  y <<- x^2+2
  return(sum(sqrt(y+7)))
}
\end{verbatim}

\section{codeListing test}
\codeListing{}{
logLik <- function(x) \{ 

~~y <<- x\^2+2

~~return(sum(sqrt(y+7)))

\}

}

\end{document}

有什么方法可以制作newcommand包装参数传递代码吗?它不必突出显示关键字或执行更高级软件包所做的任何其他技巧;它只需保留空格并且不将代码解释为 latex 命令。

答案1

我们最近考虑为 增加一些对逐字参数的支持xparse。从大约 2011 年 8 月 15 日开始(在 svn 上,尚未在 CTAN 上),您可以定义一个带有可选、强制和逐字参数的任意组合的命令。每个用 定义的命令xparse都有一个参数签名,该签名指示它期望什么类型的参数(最常见的是m普通强制参数、o可选参数s、可选星号)。例如,对于新的参数类型,\verb将有一个签名{sv}:一个可选星号后跟一个逐字参数。

对于你的情况,我认为一个好主意是将签名提供{ O{} +v O{} +v }\codeListing

  • #1, 可选参数 = 给予第一个列表的选项(默认为空);
  • #2,逐字参数 = 第一个列表的内容;
  • #3,可选参数 = 给予第二个列表的选项(默认为空);
  • #4,逐字参数 = 第二个列表的内容;

意思+是下一个参数是“长”的。对于非逐字参数,这意味着它们可能包含多个段落。对于逐字参数,它目前意味着它们可能包含换行符,与普通命令相反\verb

[除了使用 xparse 之外,我还根据 Mike Renfro 的回答给出了示例。]

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand {\codeListing} { O{} +v O{} +v }
  {
    \begin{table}[h]\centering
      \begin{tabular}[width=\textwidth]{ll}
        \newlinechar=\endlinechar
        \exp_args:Nx \scantokens
          {
            \string\begin{lstlisting}[\unexpanded{#1}]
              #2
            \string\end{lstlisting}
            &
            \string\begin{lstlisting}[\unexpanded{#3}]
              #4
            \string\end{lstlisting}
          }
      \end{tabular}
    \end{table}
  }
\ExplSyntaxOff
\usepackage[a4paper,margin=2cm]{geometry}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{lipsum}
\begin{document}
\lipsum[1]

\codeListing[language=C,backgroundcolor=\color{yellow!30}]$
#include <stdio.h>

int main(void)
{
  printf("Hello world\n");
  return 0;
}
$[language=C++,backgroundcolor=\color{red!20}]$
#include <iostream>

using namespace std;
int main()
{
  cout << "Hello World!" << endl;
  return 0;
}
$

\lipsum[2]

\end{document}

答案2

从 @xport 的示例开始,并尽可能地将其最小化,这是我能达到的最简洁的双参数命令。也许其他人可以采用剩下的方法。

在此处输入图片描述

\documentclass{article}
\usepackage[a4paper,margin=2cm]{geometry}
\usepackage{listings}

\lstnewenvironment{K}{\lstset{language=C}}{}
\lstnewenvironment{M}{\lstset{language=C++}}{}
\newenvironment{T}{\begin{table}[h]\centering\begin{tabular}[width=\textwidth]{ll}}{\end{tabular}\end{table}}

\usepackage{lipsum}
\begin{document}
\lipsum[1]

\begin{T} \begin{K}
#include <stdio.h>

int main(void)
{
  printf("Hello world\n");
  return 0;
}
\end{K} & \begin{M}
#include <iostream>

using namespace std;
int main()
{
  cout << "Hello World!" << endl;
  return 0;
}
\end{M} \end{T}

\lipsum[2]

\end{document}

答案3

想想verbatim和之类的东西listings在做什么。它们暂停了 LaTeX 对符号的正常读取,并直接打印出这些符号。所以\verb~$~告诉 LaTeX“不要解释任何直到下一个“ ~””的东西”。也就是说,LaTeX 读取\verb然后说“好的,下一个符号表示逐字文本的开始:我应该搜索该符号的下一个实例并忽略它们之间的所有内容”。然后它看到一个“ ~”,所以它会搜索下一个匹配的符号,并且不会解释它们之间的任何东西。这就是为什么\verb~\end{document}~不会结束文档的原因:LaTeX 不会解释那些通常表示文档结束的符号。

您要对 a 执行的操作\newcommand会很棘手。内容的读取顺序错误。请看以下内容:

\codeListing{$e^i$}{}

这变成:并非$e^i$如您所愿。文本未被忽略。这就是为什么您遇到了很多奇怪的事情。

另外,比较一下:

\codeListing{%}{}
\codeListing{}}{}

两者都中断了,并且:

\verb~%~
\verb~}~

它应该可以正常工作。

我认为 xport 和 Mike Renfro 的“多环境”方法在这里已经达到了最佳效果。您根本无法将逐字命令“隐藏”在新命令中:LaTeX 不知道如何处理它们。Listings 必须提供一种制作新列表环境的新方法,\lstnewenvironment因为尝试将命令放入listings命令中\newenvironment会中断...

答案4

一个可能的解决方法是使用临时文件。

首先,您需要一个宏来写入文件(\jobname.tmp这里使用),然后使用常规的listing输入命令:

\usepackage{listings}
\newwrite\tempfile
\newcommand{\example}[1]{
    \immediate\openout\tempfile=\jobname.tmp
    \immediate\write\tempfile{#1}
    \immediate\closeout\tempfile
    \input{\jobname.tmp}
    \lstinputlisting{\jobname.tmp}
}

然后在您想要放置内容的位置使用前面的命令。注意我们必须使用宏\unexpanded

\example{\unexpanded{\lipsum[1]}}

如下图所示,使用的方法与上面的方法类似:

特里

注意:lipsum对于给定的示例必须使用包。

相关内容