Quero escrever um artigo matemático com fórmulas e trechos de programas, lado a lado. Porém não quero que os programas sejam exibidos ou documentados pelo TeX, apenas poder extraí-los para um arquivo, se desejar.
Observe que minha intenção não tem nada a ver com programação literal, mas talvez eu possa (mal) usar algumas dessas ferramentas para meu propósito? Por exemplo, assumindo um token especial %# para o pré-analisador, esse arquivo poderia ter a seguinte aparência:
\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}
Minha pergunta: Existem ferramentas (ou editores) que podem preparar um documento TeX e dividi-lo em dois (ou mais) arquivos diferentes destinados ao processamento posterior por ferramentas diferentes? Ou alguma outra ideia usando recursos do TeX?
EDIT: Em extensão à excelente resposta do jfbu: Supondo que o trecho de código esteja imediatamente após uma equação numerada, o número da equação também poderia ser escrito no arquivo de código? Isto equivaleria a uma referência cruzada entre os dois resultados. (Se isso não for possível, como o contador de snippet interno introduzido na 'resposta elaborada' do jfbu pode ser escrito (com um sinal de comentário prefixado como # ou //?).
Responder1
Você pode fazer isso de dentro do tex, sem ferramentas externas.
Esta atualização gera um arquivo para cada trecho de código com numeração automática (personalizável). Este exemplo produz filename-code-01.py
, filename-code-02.py
, filename-code-03.py
, filename-code-04.py
correspondente aos quatro trechos de código (dois estão no preâmbulo).
Atualização adicional em resposta a uma edição no OP: a primeira linha de cada trecho de código de saída agora é uma linha de comentário com o número do trecho de código. A questão do uso do número da equação é mais delicada, pois a extração dos trechos de código é feita como parte ou código do preâmbulo, antes de qualquer coisa ter sido realmente escrita no documento.
$ 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
O conteúdo de preparseC-code-01.py
:
# Code snippet 1
n := 1;
for k from 1 to n
n := n * k;
A tag de linha usada para identificar trechos de código no .tex
arquivo de origem é %#<space><space>
.
Código:
\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}
Esta é a primeira versão da resposta: (removi uma \makeatletter
que era inútil e comentei a \show\LineTag
linha de depuração)
O seguinte, na compilação, também extrairá filename-code
as linhas marcadas.
n := 1;
for k from 1 to n
n := n * k;
Código:
\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}
Responder2
Existem ferramentas mais extensas, mas se você salvar seu exemplo e fff.tex
for tudo que você precisagrep
sed
grep -v "^%#" fff.tex
produz
\documentclass[]{article}
\begin{document}
The factorial is an important function:
\[
n! = \prod_{k=1}^n k
\]
\end{document}
e
grep "^%#" fff.tex | sed "s/^%#//"
produz
n := 1;
for k from 1 to n
n := n * k;
Responder3
Também pode ser feito com um script perl simples, que para mim é mais legível do que grep
e sed
com opções.
#!/usr/bin/perl
while(<STDIN>)
{
if( (substr $_, 0, 2) eq "%#" )
{
print substr $_, 2;
}
}
Documento salvo document.tex
e código perl salvo em formato extractComments.pl
.
Produz:
hpek@melda:~/programming/perl$ cat document.tex |./extractComments.pl
n := 1;
for k from 1 to n
n := n * k;
hpek@melda:~/programming/perl$
Responder4
Meus pensamentos: se o seu pedaço de código for R (http://www.r-project.org/), eu consideraria knitr
,http://yihui.name/knitr/(ou agora o velho Sweave,http://www.stat.uni-muenchen.de/~leisch/Sweave/).
Se não for, eu tentaria o extract
pacote,http://ctan.org/pkg/extract, se for adequado às suas necessidades.
Pessoalmente, eu provavelmente escolheria o listings
pacote,http://mirrors.nic.cz/tex-archive/macros/latex/contrib/listings/listings.pdf, os códigos podem ser digitados imediatamente e, se necessário, poderíamos ligar/desligar seus ambientes (então eles se comportariam como comentários), extraí-los instantaneamente e poderíamos ter pedaços de código dentro e fora do TeX principal arquivo compilável por outros programas ao mesmo tempo.