对于我从事的一些教学工作,我希望同时使用两种不同语言的代码。为此,我拼凑了一个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
对于给定的示例必须使用包。