Я хочу написать математическую статью с формулами и фрагментами программ, бок о бок. Однако я не хочу, чтобы программы отображались или документировались TeX, просто иметь возможность извлечь их в файл, если это необходимо.
Обратите внимание, что мое намерение не имеет ничего общего с буквальным программированием, но, возможно, я могу (неправильно) использовать некоторые из этих инструментов для своих целей? Например, предполагая специальный токен %# для предварительного анализатора, такой файл может выглядеть так:
\documentclass[]{article}
\begin{document}
The factorial is an important function:
\begin{equation}
n! = \prod_{k=1}^n k
\end{equation}
%# n := 1;
%# for k from 1 to n
%# n := n * k;
\end{document}
Мой вопрос: Есть ли инструменты (или редакторы), которые могут подготовить документ TeX и разделить его на два (или более) разных файла, предназначенных для дальнейшей обработки разными инструментами? Или есть какие-то другие идеи, использующие возможности TeX?
EDIT: В дополнение к превосходному ответу jfbu: Предполагая, что фрагмент кода находится сразу после пронумерованного уравнения, можем ли мы также записать номер уравнения в файл кода? Это будет равносильно перекрестной ссылке между двумя выходами. (Если это невозможно, как можно записать внутренний счетчик фрагментов, введенный в «развернутый ответ» jfbu (с предшествующим знаком комментария, например # или // ?).
решение1
Это можно сделать изнутри текса, без внешних инструментов.
Это обновление генерирует один файл для каждого фрагмента кода с автоматической (настраиваемой) нумерацией. Этот пример создает filename-code-01.py
, filename-code-02.py
, filename-code-03.py
, filename-code-04.py
соответствующие четырем фрагментам кода (два из них находятся в преамбуле).
Дополнительное обновление в ответ на редактирование OP: первая строка каждого выходного фрагмента кода теперь является строкой комментария с номером фрагмента кода. Вопрос об использовании номера уравнения более деликатный, поскольку извлечение фрагментов кода выполняется как часть кода преамбулы, до того, как что-либо будет фактически набрано в документе.
$ ls preparseC*
preparseC-code-01.py preparseC-code-04.py preparseC.log
preparseC-code-02.py preparseC.aux preparseC.tex
preparseC-code-03.py preparseC.dvi
Содержание preparseC-code-01.py
:
# Code snippet 1
n := 1;
for k from 1 to n
n := n * k;
Тег строки, используемый для идентификации фрагментов кода в .tex
исходном файле, — %#<space><space>
.
Код:
\documentclass{article}
\newcounter{snippetno}
% customize \thesnippetno as desired, for example this produces
% filename-code-01.py
% filename-code-02.py
% ...
% filename-code-10.py
% etc...
% This command should be expandable
\renewcommand{\thesnippetno}
{\jobname-code-\ifnum\value{snippetno}<10 %<- leave a space
0\fi
\arabic{snippetno}.py}
%%%%%%%% PREPARSING
\newread\parsein
\openin\parsein \jobname.tex
\newwrite\parseout
% this version will create one file for each code snippet
\newif\ifOutputtingLines
% adapt the following to the line tag you want to use
% the \detokenize is not needed here, but in case your tag
% uses letters, put them in it (do not use \catcode for letters
% a they may be in use in \def \endgroup etc..)
%
% THIS VERSION USES %#<space><space> AS LINE TAG
% (two spaces must be present and will be removed in the outputs)
\begingroup
%% ADDED DEFINITION OF \COMMENTTAG FOR USE IN FIRST LINE OF CODE SNIPPET FILES
\catcode`\% 12
\catcode`\# 12
\def\x{\endgroup\def\COMMENTTAG{#}\edef\LineTag{\detokenize{%#}\space\space}}
\x
%\show\LineTag % debugging
\begingroup
\edef\x{\endgroup
\unexpanded{\def\CheckLineAux #1}\LineTag\relax \unexpanded{{#1}}
\unexpanded{\def\CheckLine #1}\LineTag \unexpanded{#2}\relax
\unexpanded{{\if\relax #1\relax
\ifOutputtingLines\else
\stepcounter{snippetno}%
\immediate\openout\parseout \thesnippetno\relax
%% ------------------------ ADDED TO INSERT CODE SNIPPET NUMBER IN THE FILE
\immediate\write\parseout
{\COMMENTTAG\space Code snippet \arabic{snippetno}}%
%% ------------------------
\OutputtingLinestrue
\fi
\immediate\write\parseout {\CheckLineAux #2\relax}%
\else
\ifOutputtingLines
\immediate\closeout\parseout
\OutputtingLinesfalse
\fi
\fi}}%
}
\x
\begingroup\endlinechar-1
\loop
\ifeof\parsein
% if \end{document} is not missing no need to \closeout\parseout
% necessarily already done, and OutputtingLines toggle necessarily false
\closein\parsein
\else
\readline\parsein to \tmpline
\if\relax\tmpline\relax % found empty line
\ifOutputtingLines\immediate\closeout\parseout
\OutputtingLinesfalse
\fi
\else
\expandafter\expandafter\expandafter \CheckLine
\expandafter \tmpline\LineTag \relax
\fi
\repeat
\endgroup
%%%%%%%% END OF PREPARSING
% Some code snippets may already be put here in the preamble
%
%# n := 1;
%# for k from 1 to n
%# n := n * k;
%# y := 1;
%# for k from 1 to n
%# y := y * (x + k -1);
% Notice that in this variant the line tag is %#<space><space>
% and is removed on output
\begin{document}
The factorial is an important function:
\[
n! = \prod_{k=1}^n k
\]
%# n := 1;
%# for k from 1 to n
%# n := n * k;
The (so-called) Pochhammer coefficient also:
\[
(x)_n = \prod_{k=1}^n (x+k-1)
\]
%# y := 1;
%# for k from 1 to n
%# y := y * (x + k -1);
\end{document}
Это первая версия ответа: (я удалил a, \makeatletter
который был бесполезен, и закомментировал \show\LineTag
строку отладки)
Следующий код при компиляции также будет извлечен в filename-code
отмеченные строки.
n := 1;
for k from 1 to n
n := n * k;
Код:
\documentclass{article}
%%%%%%%% PREPARSING
\newread\parsein
\openin\parsein \jobname.tex
\newwrite\parseout
\immediate\openout\parseout \jobname-code
% adapt the following to the line tag you want to use
% the \detokenize is not needed here, but in case your tag
% uses letters, put them in it (do not use \catcode for letters
% a they may be in use in \def \endgroup etc..)
\begingroup
\catcode`\% 12
\catcode`\# 12
\def\x{\endgroup\edef\LineTag{\detokenize{%#}}}
\x
%\show\LineTag % debugging
\begingroup
\edef\x{\endgroup
\unexpanded{\def\CheckLineAux #1}\LineTag\relax \unexpanded{{#1}}
\unexpanded{\def\CheckLine #1}\LineTag \unexpanded{#2}\relax
\unexpanded{{\if\relax #1\relax
\immediate\write\parseout {\CheckLineAux #2\relax}%
\fi}}%
}
\x
\begingroup\endlinechar-1
\loop
\ifeof\parsein
\immediate\closeout\parseout
\closein\parsein
\else
\readline\parsein to \tmpline
\if\relax\tmpline\relax\else
\expandafter\expandafter\expandafter \CheckLine
\expandafter \tmpline\LineTag \relax
\fi
\repeat
\endgroup
%%%%%%%% END OF PREPARSING
\begin{document}
The factorial is an important function:
\[
n! = \prod_{k=1}^n k
\]
%# n := 1;
%# for k from 1 to n
%# n := n * k;
\end{document}
решение2
Есть более обширные инструменты, но если вы сохраните свой пример, то fff.tex
это grep
все sed
, что вам нужно.
grep -v "^%#" fff.tex
производит
\documentclass[]{article}
\begin{document}
The factorial is an important function:
\[
n! = \prod_{k=1}^n k
\]
\end{document}
и
grep "^%#" fff.tex | sed "s/^%#//"
производит
n := 1;
for k from 1 to n
n := n * k;
решение3
Это также можно сделать с помощью простого скрипта Perl, который, на мой взгляд, более удобочитаем, чем grep
и sed
с опциями.
#!/usr/bin/perl
while(<STDIN>)
{
if( (substr $_, 0, 2) eq "%#" )
{
print substr $_, 2;
}
}
Документ сохранен в формате document.tex
, а код Perl сохранен в формате extractComments.pl
.
Производит:
hpek@melda:~/programming/perl$ cat document.tex |./extractComments.pl
n := 1;
for k from 1 to n
n := n * k;
hpek@melda:~/programming/perl$
решение4
Мои мысли: Если ваш фрагмент кода — R (http://www.r-project.org/), я бы рассмотрел knitr
,http://yihui.name/knitr/(или теперь скорее старый Суэйв,http://www.stat.uni-muenchen.de/~leisch/Sweave/).
Если нет, я бы попробовал пакет extract
,http://ctan.org/pkg/extract, если это соответствует вашим потребностям.
Лично я бы, наверное, выбрал пакет listings
,http://mirrors.nic.cz/tex-archive/macros/latex/contrib/listings/listings.pdf, коды можно набирать сразу, и при необходимости можно включать/выключать их окружения (тогда они будут вести себя как комментарии), извлекать их на лету, и можно иметь фрагменты кода внутри и вне основного файла TeX, которые можно будет компилировать другими программами одновременно.